summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlexander Sulfrian <alexander@sulfrian.net>2012-09-26 13:05:30 +0200
committerAlexander Sulfrian <alexander@sulfrian.net>2012-09-26 13:10:05 +0200
commit0be114db112f43f3712033d3531f955412c1f6f3 (patch)
treebcf856a8237ef0d380a97a1670d82bf6ad4d0a48
downloadldap-0be114db112f43f3712033d3531f955412c1f6f3.tar.gz
ldap-0be114db112f43f3712033d3531f955412c1f6f3.tar.bz2
ldap-0be114db112f43f3712033d3531f955412c1f6f3.zip
inital version (2.4.23)
-rw-r--r--rwm.c2711
-rw-r--r--rwm.h187
-rw-r--r--rwmconf.c417
-rw-r--r--rwmdn.c215
-rw-r--r--rwmmap.c1313
-rw-r--r--translucent.c1434
6 files changed, 6277 insertions, 0 deletions
diff --git a/rwm.c b/rwm.c
new file mode 100644
index 0000000..2bb7371
--- /dev/null
+++ b/rwm.c
@@ -0,0 +1,2711 @@
+/* rwm.c - rewrite/remap operations */
+/* $OpenLDAP: pkg/ldap/servers/slapd/overlays/rwm.c,v 1.70.2.36 2010/06/10 17:37:40 quanah Exp $ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2003-2010 The OpenLDAP Foundation.
+ * Portions Copyright 2003 Pierangelo Masarati.
+ * 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>.
+ */
+
+#include "portable.h"
+
+#ifdef SLAPD_OVER_RWM
+
+#include <stdio.h>
+
+#include <ac/string.h>
+
+#include "slap.h"
+#include "config.h"
+#include "lutil.h"
+#include "rwm.h"
+
+typedef struct rwm_op_state {
+ ber_tag_t r_tag;
+ struct berval ro_dn;
+ struct berval ro_ndn;
+ struct berval r_dn;
+ struct berval r_ndn;
+ AttributeName *mapped_attrs;
+ OpRequest o_request;
+} rwm_op_state;
+
+typedef struct rwm_op_cb {
+ slap_callback cb;
+ rwm_op_state ros;
+} rwm_op_cb;
+
+static int
+rwm_db_destroy( BackendDB *be, ConfigReply *cr );
+
+static int
+rwm_send_entry( Operation *op, SlapReply *rs );
+
+static void
+rwm_op_rollback( Operation *op, SlapReply *rs, rwm_op_state *ros )
+{
+ if ( !BER_BVISNULL( &ros->ro_dn ) ) {
+ op->o_req_dn = ros->ro_dn;
+ }
+ if ( !BER_BVISNULL( &ros->ro_ndn ) ) {
+ op->o_req_ndn = ros->ro_ndn;
+ }
+
+ if ( !BER_BVISNULL( &ros->r_dn )
+ && ros->r_dn.bv_val != ros->ro_dn.bv_val )
+ {
+ assert( ros->r_dn.bv_val != ros->r_ndn.bv_val );
+ ch_free( ros->r_dn.bv_val );
+ BER_BVZERO( &ros->r_dn );
+ }
+
+ if ( !BER_BVISNULL( &ros->r_ndn )
+ && ros->r_ndn.bv_val != ros->ro_ndn.bv_val )
+ {
+ ch_free( ros->r_ndn.bv_val );
+ BER_BVZERO( &ros->r_ndn );
+ }
+
+ BER_BVZERO( &ros->ro_dn );
+ BER_BVZERO( &ros->ro_ndn );
+
+ switch( ros->r_tag ) {
+ case LDAP_REQ_COMPARE:
+ if ( op->orc_ava->aa_value.bv_val != ros->orc_ava->aa_value.bv_val )
+ op->o_tmpfree( op->orc_ava->aa_value.bv_val, op->o_tmpmemctx );
+ op->orc_ava = ros->orc_ava;
+ break;
+ case LDAP_REQ_MODIFY:
+ slap_mods_free( op->orm_modlist, 1 );
+ op->orm_modlist = ros->orm_modlist;
+ break;
+ case LDAP_REQ_MODRDN:
+ if ( op->orr_newSup != ros->orr_newSup ) {
+ ch_free( op->orr_newSup->bv_val );
+ ch_free( op->orr_nnewSup->bv_val );
+ op->o_tmpfree( op->orr_newSup, op->o_tmpmemctx );
+ op->o_tmpfree( op->orr_nnewSup, op->o_tmpmemctx );
+ op->orr_newSup = ros->orr_newSup;
+ op->orr_nnewSup = ros->orr_nnewSup;
+ }
+ if ( op->orr_newrdn.bv_val != ros->orr_newrdn.bv_val ) {
+ ch_free( op->orr_newrdn.bv_val );
+ ch_free( op->orr_nnewrdn.bv_val );
+ op->orr_newrdn = ros->orr_newrdn;
+ op->orr_nnewrdn = ros->orr_nnewrdn;
+ }
+ break;
+ case LDAP_REQ_SEARCH:
+ op->o_tmpfree( ros->mapped_attrs, op->o_tmpmemctx );
+ filter_free_x( op, op->ors_filter, 1 );
+ op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
+ op->ors_attrs = ros->ors_attrs;
+ op->ors_filter = ros->ors_filter;
+ op->ors_filterstr = ros->ors_filterstr;
+ break;
+ case LDAP_REQ_EXTENDED:
+ if ( op->ore_reqdata != ros->ore_reqdata ) {
+ ber_bvfree( op->ore_reqdata );
+ op->ore_reqdata = ros->ore_reqdata;
+ }
+ break;
+ case LDAP_REQ_BIND:
+ if ( rs->sr_err == LDAP_SUCCESS ) {
+#if 0
+ ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex );
+ /* too late, c_mutex released */
+ Debug( LDAP_DEBUG_ANY, "*** DN: \"%s\" => \"%s\"\n",
+ op->o_conn->c_ndn.bv_val,
+ op->o_req_ndn.bv_val );
+ ber_bvreplace( &op->o_conn->c_ndn,
+ &op->o_req_ndn );
+ ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );
+#endif
+ }
+ break;
+ default: break;
+ }
+}
+
+static int
+rwm_op_cleanup( Operation *op, SlapReply *rs )
+{
+ slap_callback *cb = op->o_callback;
+ rwm_op_state *ros = cb->sc_private;
+
+ if ( rs->sr_type == REP_RESULT || rs->sr_type == REP_EXTENDED ||
+ op->o_abandon || rs->sr_err == SLAPD_ABANDON )
+ {
+ rwm_op_rollback( op, rs, ros );
+
+ op->o_callback = op->o_callback->sc_next;
+ op->o_tmpfree( cb, op->o_tmpmemctx );
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+static rwm_op_cb *
+rwm_callback_get( Operation *op, SlapReply *rs )
+{
+ rwm_op_cb *roc = NULL;
+
+ roc = op->o_tmpalloc( sizeof( struct rwm_op_cb ), op->o_tmpmemctx );
+ roc->cb.sc_cleanup = rwm_op_cleanup;
+ roc->cb.sc_response = NULL;
+ roc->cb.sc_next = op->o_callback;
+ roc->cb.sc_private = &roc->ros;
+ roc->ros.r_tag = op->o_tag;
+ roc->ros.ro_dn = op->o_req_dn;
+ roc->ros.ro_ndn = op->o_req_ndn;
+ roc->ros.o_request = op->o_request;
+ BER_BVZERO( &roc->ros.r_dn );
+ BER_BVZERO( &roc->ros.r_ndn );
+
+ return roc;
+}
+
+
+static int
+rwm_op_dn_massage( Operation *op, SlapReply *rs, void *cookie,
+ rwm_op_state *ros )
+{
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ struct ldaprwmap *rwmap =
+ (struct ldaprwmap *)on->on_bi.bi_private;
+
+ struct berval dn = BER_BVNULL,
+ ndn = BER_BVNULL;
+ int rc = 0;
+ dncookie dc;
+
+ /*
+ * Rewrite the dn if needed
+ */
+ dc.rwmap = rwmap;
+ dc.conn = op->o_conn;
+ dc.rs = rs;
+ dc.ctx = (char *)cookie;
+
+ /* NOTE: in those cases where only the ndn is available,
+ * and the caller sets op->o_req_dn = op->o_req_ndn,
+ * only rewrite the op->o_req_ndn and use it as
+ * op->o_req_dn as well */
+ ndn = op->o_req_ndn;
+ if ( op->o_req_dn.bv_val != op->o_req_ndn.bv_val ) {
+ dn = op->o_req_dn;
+ rc = rwm_dn_massage_pretty_normalize( &dc, &op->o_req_dn, &dn, &ndn );
+ } else {
+ rc = rwm_dn_massage_normalize( &dc, &op->o_req_ndn, &ndn );
+ }
+
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+
+ if ( ( op->o_req_dn.bv_val != op->o_req_ndn.bv_val && dn.bv_val == op->o_req_dn.bv_val )
+ || ndn.bv_val == op->o_req_ndn.bv_val )
+ {
+ return LDAP_SUCCESS;
+ }
+
+ if ( op->o_req_dn.bv_val != op->o_req_ndn.bv_val ) {
+ op->o_req_dn = dn;
+ assert( BER_BVISNULL( &ros->r_dn ) );
+ ros->r_dn = dn;
+ } else {
+ op->o_req_dn = ndn;
+ }
+ op->o_req_ndn = ndn;
+ assert( BER_BVISNULL( &ros->r_ndn ) );
+ ros->r_ndn = ndn;
+
+ return LDAP_SUCCESS;
+}
+
+static int
+rwm_op_add( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ struct ldaprwmap *rwmap =
+ (struct ldaprwmap *)on->on_bi.bi_private;
+
+ int rc,
+ i;
+ Attribute **ap = NULL;
+ char *olddn = op->o_req_dn.bv_val;
+ int isupdate;
+
+ rwm_op_cb *roc = rwm_callback_get( op, rs );
+
+ rc = rwm_op_dn_massage( op, rs, "addDN", &roc->ros );
+ if ( rc != LDAP_SUCCESS ) {
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+ send_ldap_error( op, rs, rc, "addDN massage error" );
+ return -1;
+ }
+
+ if ( olddn != op->o_req_dn.bv_val ) {
+ ber_bvreplace( &op->ora_e->e_name, &op->o_req_dn );
+ ber_bvreplace( &op->ora_e->e_nname, &op->o_req_ndn );
+ }
+
+ /* Count number of attributes in entry */
+ isupdate = be_shadow_update( op );
+ for ( i = 0, ap = &op->oq_add.rs_e->e_attrs; *ap; ) {
+ Attribute *a;
+
+ if ( (*ap)->a_desc == slap_schema.si_ad_objectClass ||
+ (*ap)->a_desc == slap_schema.si_ad_structuralObjectClass )
+ {
+ int j, last;
+
+ last = (*ap)->a_numvals - 1;
+ for ( j = 0; !BER_BVISNULL( &(*ap)->a_vals[ j ] ); j++ ) {
+ struct ldapmapping *mapping = NULL;
+
+ ( void )rwm_mapping( &rwmap->rwm_oc, &(*ap)->a_vals[ j ],
+ &mapping, RWM_MAP );
+ if ( mapping == NULL ) {
+ if ( rwmap->rwm_at.drop_missing ) {
+ /* FIXME: we allow to remove objectClasses as well;
+ * if the resulting entry is inconsistent, that's
+ * the relayed database's business...
+ */
+ ch_free( (*ap)->a_vals[ j ].bv_val );
+ if ( last > j ) {
+ (*ap)->a_vals[ j ] = (*ap)->a_vals[ last ];
+ }
+ BER_BVZERO( &(*ap)->a_vals[ last ] );
+ (*ap)->a_numvals--;
+ last--;
+ j--;
+ }
+
+ } else {
+ ch_free( (*ap)->a_vals[ j ].bv_val );
+ ber_dupbv( &(*ap)->a_vals[ j ], &mapping->m_dst );
+ }
+ }
+
+ } else if ( !isupdate && !get_relax( op ) && (*ap)->a_desc->ad_type->sat_no_user_mod )
+ {
+ goto next_attr;
+
+ } else {
+ struct ldapmapping *mapping = NULL;
+
+ ( void )rwm_mapping( &rwmap->rwm_at, &(*ap)->a_desc->ad_cname,
+ &mapping, RWM_MAP );
+ if ( mapping == NULL ) {
+ if ( rwmap->rwm_at.drop_missing ) {
+ goto cleanup_attr;
+ }
+ }
+
+ if ( (*ap)->a_desc->ad_type->sat_syntax == slap_schema.si_syn_distinguishedName
+ || ( mapping != NULL && mapping->m_dst_ad->ad_type->sat_syntax == slap_schema.si_syn_distinguishedName ) )
+ {
+ /*
+ * FIXME: rewrite could fail; in this case
+ * the operation should give up, right?
+ */
+ rc = rwm_dnattr_rewrite( op, rs, "addAttrDN",
+ (*ap)->a_vals,
+ (*ap)->a_nvals ? &(*ap)->a_nvals : NULL );
+ if ( rc ) {
+ goto cleanup_attr;
+ }
+
+ } else if ( (*ap)->a_desc == slap_schema.si_ad_ref ) {
+ rc = rwm_referral_rewrite( op, rs, "referralAttrDN",
+ (*ap)->a_vals,
+ (*ap)->a_nvals ? &(*ap)->a_nvals : NULL );
+ if ( rc != LDAP_SUCCESS ) {
+ goto cleanup_attr;
+ }
+ }
+
+ if ( mapping != NULL ) {
+ assert( mapping->m_dst_ad != NULL );
+ (*ap)->a_desc = mapping->m_dst_ad;
+ }
+ }
+
+next_attr:;
+ ap = &(*ap)->a_next;
+ continue;
+
+cleanup_attr:;
+ /* FIXME: leaking attribute/values? */
+ a = *ap;
+
+ *ap = (*ap)->a_next;
+ attr_free( a );
+ }
+
+ op->o_callback = &roc->cb;
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+rwm_conn_init( BackendDB *be, Connection *conn )
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+ struct ldaprwmap *rwmap =
+ (struct ldaprwmap *)on->on_bi.bi_private;
+
+ ( void )rewrite_session_init( rwmap->rwm_rw, conn );
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+rwm_conn_destroy( BackendDB *be, Connection *conn )
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+ struct ldaprwmap *rwmap =
+ (struct ldaprwmap *)on->on_bi.bi_private;
+
+ ( void )rewrite_session_delete( rwmap->rwm_rw, conn );
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+rwm_op_bind( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ int rc;
+
+ rwm_op_cb *roc = rwm_callback_get( op, rs );
+
+ rc = rwm_op_dn_massage( op, rs, "bindDN", &roc->ros );
+ if ( rc != LDAP_SUCCESS ) {
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+ send_ldap_error( op, rs, rc, "bindDN massage error" );
+ return -1;
+ }
+
+ overlay_callback_after_backover( op, &roc->cb, 1 );
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+rwm_op_unbind( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ struct ldaprwmap *rwmap =
+ (struct ldaprwmap *)on->on_bi.bi_private;
+
+ rewrite_session_delete( rwmap->rwm_rw, op->o_conn );
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+rwm_op_compare( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ struct ldaprwmap *rwmap =
+ (struct ldaprwmap *)on->on_bi.bi_private;
+
+ int rc;
+ struct berval mapped_vals[2] = { BER_BVNULL, BER_BVNULL };
+
+ rwm_op_cb *roc = rwm_callback_get( op, rs );
+
+ rc = rwm_op_dn_massage( op, rs, "compareDN", &roc->ros );
+ if ( rc != LDAP_SUCCESS ) {
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+ send_ldap_error( op, rs, rc, "compareDN massage error" );
+ return -1;
+ }
+
+ /* if the attribute is an objectClass, try to remap its value */
+ if ( op->orc_ava->aa_desc == slap_schema.si_ad_objectClass
+ || op->orc_ava->aa_desc == slap_schema.si_ad_structuralObjectClass )
+ {
+ rwm_map( &rwmap->rwm_oc, &op->orc_ava->aa_value,
+ &mapped_vals[0], RWM_MAP );
+ if ( BER_BVISNULL( &mapped_vals[0] ) || BER_BVISEMPTY( &mapped_vals[0] ) )
+ {
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+ send_ldap_error( op, rs, LDAP_OTHER, "compare objectClass map error" );
+ return -1;
+
+ } else if ( mapped_vals[0].bv_val != op->orc_ava->aa_value.bv_val ) {
+ ber_dupbv_x( &op->orc_ava->aa_value, &mapped_vals[0],
+ op->o_tmpmemctx );
+ }
+
+ } else {
+ struct ldapmapping *mapping = NULL;
+ AttributeDescription *ad = op->orc_ava->aa_desc;
+
+ ( void )rwm_mapping( &rwmap->rwm_at, &op->orc_ava->aa_desc->ad_cname,
+ &mapping, RWM_MAP );
+ if ( mapping == NULL ) {
+ if ( rwmap->rwm_at.drop_missing ) {
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+ send_ldap_error( op, rs, LDAP_OTHER, "compare attributeType map error" );
+ return -1;
+ }
+
+ } else {
+ assert( mapping->m_dst_ad != NULL );
+ ad = mapping->m_dst_ad;
+ }
+
+ if ( op->orc_ava->aa_desc->ad_type->sat_syntax == slap_schema.si_syn_distinguishedName
+ || ( mapping != NULL && mapping->m_dst_ad->ad_type->sat_syntax == slap_schema.si_syn_distinguishedName ) )
+ {
+ struct berval *mapped_valsp[2];
+
+ mapped_valsp[0] = &mapped_vals[0];
+ mapped_valsp[1] = &mapped_vals[1];
+
+ mapped_vals[0] = op->orc_ava->aa_value;
+
+ rc = rwm_dnattr_rewrite( op, rs, "compareAttrDN", NULL, mapped_valsp );
+
+ if ( rc != LDAP_SUCCESS ) {
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+ send_ldap_error( op, rs, rc, "compareAttrDN massage error" );
+ return -1;
+ }
+
+ if ( mapped_vals[ 0 ].bv_val != op->orc_ava->aa_value.bv_val ) {
+ /* NOTE: if we get here, rwm_dnattr_rewrite()
+ * already freed the old value, so now
+ * it's invalid */
+ ber_dupbv_x( &op->orc_ava->aa_value, &mapped_vals[0],
+ op->o_tmpmemctx );
+ ber_memfree_x( mapped_vals[ 0 ].bv_val, NULL );
+ }
+ }
+ op->orc_ava->aa_desc = ad;
+ }
+
+ op->o_callback = &roc->cb;
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+rwm_op_delete( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ int rc;
+
+ rwm_op_cb *roc = rwm_callback_get( op, rs );
+
+ rc = rwm_op_dn_massage( op, rs, "deleteDN", &roc->ros );
+ if ( rc != LDAP_SUCCESS ) {
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+ send_ldap_error( op, rs, rc, "deleteDN massage error" );
+ return -1;
+ }
+
+ op->o_callback = &roc->cb;
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+rwm_op_modify( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ struct ldaprwmap *rwmap =
+ (struct ldaprwmap *)on->on_bi.bi_private;
+
+ int isupdate;
+ Modifications **mlp;
+ int rc;
+
+ rwm_op_cb *roc = rwm_callback_get( op, rs );
+
+ rc = rwm_op_dn_massage( op, rs, "modifyDN", &roc->ros );
+ if ( rc != LDAP_SUCCESS ) {
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+ send_ldap_error( op, rs, rc, "modifyDN massage error" );
+ return -1;
+ }
+
+ isupdate = be_shadow_update( op );
+ for ( mlp = &op->orm_modlist; *mlp; ) {
+ int is_oc = 0;
+ Modifications *ml = *mlp;
+ struct ldapmapping *mapping = NULL;
+
+ /* ml points to a temporary mod until needs duplication */
+ if ( ml->sml_desc == slap_schema.si_ad_objectClass
+ || ml->sml_desc == slap_schema.si_ad_structuralObjectClass )
+ {
+ is_oc = 1;
+
+ } else if ( !isupdate && !get_relax( op ) && ml->sml_desc->ad_type->sat_no_user_mod )
+ {
+ ml = ch_malloc( sizeof( Modifications ) );
+ *ml = **mlp;
+ if ( (*mlp)->sml_values ) {
+ ber_bvarray_dup_x( &ml->sml_values, (*mlp)->sml_values, NULL );
+ if ( (*mlp)->sml_nvalues ) {
+ ber_bvarray_dup_x( &ml->sml_nvalues, (*mlp)->sml_nvalues, NULL );
+ }
+ }
+ *mlp = ml;
+ goto next_mod;
+
+ } else {
+ int drop_missing;
+
+ drop_missing = rwm_mapping( &rwmap->rwm_at,
+ &ml->sml_desc->ad_cname,
+ &mapping, RWM_MAP );
+ if ( drop_missing || ( mapping != NULL && BER_BVISNULL( &mapping->m_dst ) ) )
+ {
+ goto cleanup_mod;
+ }
+ }
+
+ /* duplicate the modlist */
+ ml = ch_malloc( sizeof( Modifications ));
+ *ml = **mlp;
+ *mlp = ml;
+
+ if ( ml->sml_values != NULL ) {
+ int i, num;
+ struct berval *bva;
+
+ for ( num = 0; !BER_BVISNULL( &ml->sml_values[ num ] ); num++ )
+ /* count values */ ;
+
+ bva = ch_malloc( (num+1) * sizeof( struct berval ));
+ for (i=0; i<num; i++)
+ ber_dupbv( &bva[i], &ml->sml_values[i] );
+ BER_BVZERO( &bva[i] );
+ ml->sml_values = bva;
+
+ if ( ml->sml_nvalues ) {
+ bva = ch_malloc( (num+1) * sizeof( struct berval ));
+ for (i=0; i<num; i++)
+ ber_dupbv( &bva[i], &ml->sml_nvalues[i] );
+ BER_BVZERO( &bva[i] );
+ ml->sml_nvalues = bva;
+ }
+
+ if ( is_oc ) {
+ int last, j;
+
+ last = num-1;
+
+ for ( j = 0; !BER_BVISNULL( &ml->sml_values[ j ] ); j++ ) {
+ struct ldapmapping *oc_mapping = NULL;
+
+ ( void )rwm_mapping( &rwmap->rwm_oc, &ml->sml_values[ j ],
+ &oc_mapping, RWM_MAP );
+ if ( oc_mapping == NULL ) {
+ if ( rwmap->rwm_at.drop_missing ) {
+ /* FIXME: we allow to remove objectClasses as well;
+ * if the resulting entry is inconsistent, that's
+ * the relayed database's business...
+ */
+ if ( last > j ) {
+ ch_free( ml->sml_values[ j ].bv_val );
+ ml->sml_values[ j ] = ml->sml_values[ last ];
+ }
+ BER_BVZERO( &ml->sml_values[ last ] );
+ last--;
+ j--;
+ }
+
+ } else {
+ ch_free( ml->sml_values[ j ].bv_val );
+ ber_dupbv( &ml->sml_values[ j ], &oc_mapping->m_dst );
+ }
+ }
+
+ } else {
+ if ( ml->sml_desc->ad_type->sat_syntax == slap_schema.si_syn_distinguishedName
+ || ( mapping != NULL && mapping->m_dst_ad->ad_type->sat_syntax == slap_schema.si_syn_distinguishedName ) )
+ {
+ rc = rwm_dnattr_rewrite( op, rs, "modifyAttrDN",
+ ml->sml_values,
+ ml->sml_nvalues ? &ml->sml_nvalues : NULL );
+
+ } else if ( ml->sml_desc == slap_schema.si_ad_ref ) {
+ rc = rwm_referral_rewrite( op, rs,
+ "referralAttrDN",
+ ml->sml_values,
+ ml->sml_nvalues ? &ml->sml_nvalues : NULL );
+ if ( rc != LDAP_SUCCESS ) {
+ goto cleanup_mod;
+ }
+ }
+
+ if ( rc != LDAP_SUCCESS ) {
+ goto cleanup_mod;
+ }
+ }
+ }
+
+next_mod:;
+ if ( mapping != NULL ) {
+ /* use new attribute description */
+ assert( mapping->m_dst_ad != NULL );
+ ml->sml_desc = mapping->m_dst_ad;
+ }
+
+ mlp = &ml->sml_next;
+ continue;
+
+cleanup_mod:;
+ ml = *mlp;
+ *mlp = (*mlp)->sml_next;
+ slap_mod_free( &ml->sml_mod, 0 );
+ free( ml );
+ }
+
+ op->o_callback = &roc->cb;
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+rwm_op_modrdn( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ struct ldaprwmap *rwmap =
+ (struct ldaprwmap *)on->on_bi.bi_private;
+
+ int rc;
+ dncookie dc;
+
+ rwm_op_cb *roc = rwm_callback_get( op, rs );
+
+ if ( op->orr_newSup ) {
+ struct berval nnewSup = BER_BVNULL;
+ struct berval newSup = BER_BVNULL;
+
+ /*
+ * Rewrite the new superior, if defined and required
+ */
+ dc.rwmap = rwmap;
+ dc.conn = op->o_conn;
+ dc.rs = rs;
+ dc.ctx = "newSuperiorDN";
+ newSup = *op->orr_newSup;
+ nnewSup = *op->orr_nnewSup;
+ rc = rwm_dn_massage_pretty_normalize( &dc, op->orr_newSup, &newSup, &nnewSup );
+ if ( rc != LDAP_SUCCESS ) {
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+ send_ldap_error( op, rs, rc, "newSuperiorDN massage error" );
+ return -1;
+ }
+
+ if ( op->orr_newSup->bv_val != newSup.bv_val ) {
+ op->orr_newSup = op->o_tmpalloc( sizeof( struct berval ),
+ op->o_tmpmemctx );
+ op->orr_nnewSup = op->o_tmpalloc( sizeof( struct berval ),
+ op->o_tmpmemctx );
+ *op->orr_newSup = newSup;
+ *op->orr_nnewSup = nnewSup;
+ }
+ }
+
+ /*
+ * Rewrite the newRDN, if needed
+ */
+ {
+ struct berval newrdn = BER_BVNULL;
+ struct berval nnewrdn = BER_BVNULL;
+
+ dc.rwmap = rwmap;
+ dc.conn = op->o_conn;
+ dc.rs = rs;
+ dc.ctx = "newRDN";
+ newrdn = op->orr_newrdn;
+ nnewrdn = op->orr_nnewrdn;
+ rc = rwm_dn_massage_pretty_normalize( &dc, &op->orr_newrdn, &newrdn, &nnewrdn );
+ if ( rc != LDAP_SUCCESS ) {
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+ send_ldap_error( op, rs, rc, "newRDN massage error" );
+ goto err;
+ }
+
+ if ( op->orr_newrdn.bv_val != newrdn.bv_val ) {
+ op->orr_newrdn = newrdn;
+ op->orr_nnewrdn = nnewrdn;
+ }
+ }
+
+ /*
+ * Rewrite the dn, if needed
+ */
+ rc = rwm_op_dn_massage( op, rs, "renameDN", &roc->ros );
+ if ( rc != LDAP_SUCCESS ) {
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+ send_ldap_error( op, rs, rc, "renameDN massage error" );
+ goto err;
+ }
+
+ op->o_callback = &roc->cb;
+
+ rc = SLAP_CB_CONTINUE;
+
+ if ( 0 ) {
+err:;
+ if ( op->orr_newSup != roc->ros.orr_newSup ) {
+ ch_free( op->orr_newSup->bv_val );
+ ch_free( op->orr_nnewSup->bv_val );
+ op->o_tmpfree( op->orr_newSup, op->o_tmpmemctx );
+ op->o_tmpfree( op->orr_nnewSup, op->o_tmpmemctx );
+ op->orr_newSup = roc->ros.orr_newSup;
+ op->orr_nnewSup = roc->ros.orr_nnewSup;
+ }
+
+ if ( op->orr_newrdn.bv_val != roc->ros.orr_newrdn.bv_val ) {
+ ch_free( op->orr_newrdn.bv_val );
+ ch_free( op->orr_nnewrdn.bv_val );
+ op->orr_newrdn = roc->ros.orr_newrdn;
+ op->orr_nnewrdn = roc->ros.orr_nnewrdn;
+ }
+ }
+
+ return rc;
+}
+
+
+static int
+rwm_swap_attrs( Operation *op, SlapReply *rs )
+{
+ slap_callback *cb = op->o_callback;
+ rwm_op_state *ros = cb->sc_private;
+
+ rs->sr_attrs = ros->ors_attrs;
+
+ /* other overlays might have touched op->ors_attrs,
+ * so we restore the original version here, otherwise
+ * attribute-mapping might fail */
+ op->ors_attrs = ros->mapped_attrs;
+
+ return SLAP_CB_CONTINUE;
+}
+
+/*
+ * NOTE: this implementation of get/release entry is probably far from
+ * optimal. The rationale consists in intercepting the request directed
+ * to the underlying database, in order to rewrite/remap the request,
+ * perform it using the modified data, duplicate the resulting entry
+ * and finally free it when release is called.
+ * This implies that subsequent overlays are not called, as the request
+ * is directly shunted to the underlying database.
+ */
+static int
+rwm_entry_release_rw( Operation *op, Entry *e, int rw )
+{
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+
+ /* can't be ours */
+ if ( ((BackendInfo *)on->on_info->oi_orig)->bi_entry_get_rw == NULL ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ /* just free entry if (probably) ours */
+ if ( e->e_private == NULL && BER_BVISNULL( &e->e_bv ) ) {
+ entry_free( e );
+ return LDAP_SUCCESS;
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+rwm_entry_get_rw( Operation *op, struct berval *ndn,
+ ObjectClass *oc, AttributeDescription *at, int rw, Entry **ep )
+{
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ struct ldaprwmap *rwmap =
+ (struct ldaprwmap *)on->on_bi.bi_private;
+
+ int rc;
+ dncookie dc;
+
+ BackendDB db;
+ Operation op2;
+ SlapReply rs = { REP_SEARCH };
+
+ rwm_op_state ros = { 0 };
+ struct berval mndn = BER_BVNULL;
+
+ if ( ((BackendInfo *)on->on_info->oi_orig)->bi_entry_get_rw == NULL ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ /* massage DN */
+ op2.o_tag = LDAP_REQ_SEARCH;
+ op2 = *op;
+ op2.o_req_dn = *ndn;
+ op2.o_req_ndn = *ndn;
+ rc = rwm_op_dn_massage( &op2, &rs, "searchDN", &ros );
+ if ( rc != LDAP_SUCCESS ) {
+ return LDAP_OTHER;
+ }
+
+ mndn = BER_BVISNULL( &ros.r_ndn ) ? *ndn : ros.r_ndn;
+
+ /* map attribute & objectClass */
+ if ( at != NULL ) {
+ }
+
+ if ( oc != NULL ) {
+ }
+
+ /* fetch entry */
+ db = *op->o_bd;
+ op2.o_bd = &db;
+ op2.o_bd->bd_info = (BackendInfo *)on->on_info->oi_orig;
+ op2.ors_attrs = slap_anlist_all_attributes;
+ rc = op2.o_bd->bd_info->bi_entry_get_rw( &op2, &mndn, oc, at, rw, ep );
+ if ( rc == LDAP_SUCCESS && *ep != NULL ) {
+ /* we assume be_entry_release() needs to be called */
+ rs.sr_flags = REP_ENTRY_MUSTRELEASE;
+ rs.sr_entry = *ep;
+
+ /* duplicate & release */
+ op2.o_bd->bd_info = (BackendInfo *)on;
+ rc = rwm_send_entry( &op2, &rs );
+ if ( rc == SLAP_CB_CONTINUE ) {
+ *ep = rs.sr_entry;
+ rc = LDAP_SUCCESS;
+ }
+ }
+
+ if ( !BER_BVISNULL( &ros.r_ndn) && ros.r_ndn.bv_val != ndn->bv_val ) {
+ op->o_tmpfree( ros.r_ndn.bv_val, op->o_tmpmemctx );
+ }
+
+ return rc;
+}
+
+static int
+rwm_op_search( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ struct ldaprwmap *rwmap =
+ (struct ldaprwmap *)on->on_bi.bi_private;
+
+ int rc;
+ dncookie dc;
+
+ struct berval fstr = BER_BVNULL;
+ Filter *f = NULL;
+
+ AttributeName *an = NULL;
+
+ char *text = NULL;
+
+ rwm_op_cb *roc = rwm_callback_get( op, rs );
+
+ rc = rewrite_session_var_set( rwmap->rwm_rw, op->o_conn,
+ "searchFilter", op->ors_filterstr.bv_val );
+ if ( rc == LDAP_SUCCESS )
+ rc = rwm_op_dn_massage( op, rs, "searchDN", &roc->ros );
+ if ( rc != LDAP_SUCCESS ) {
+ text = "searchDN massage error";
+ goto error_return;
+ }
+
+ /*
+ * Rewrite the dn if needed
+ */
+ dc.rwmap = rwmap;
+ dc.conn = op->o_conn;
+ dc.rs = rs;
+ dc.ctx = "searchFilterAttrDN";
+
+ rc = rwm_filter_map_rewrite( op, &dc, op->ors_filter, &fstr );
+ if ( rc != LDAP_SUCCESS ) {
+ text = "searchFilter/searchFilterAttrDN massage error";
+ goto error_return;
+ }
+
+ f = str2filter_x( op, fstr.bv_val );
+
+ if ( f == NULL ) {
+ text = "massaged filter parse error";
+ goto error_return;
+ }
+
+ op->ors_filter = f;
+ op->ors_filterstr = fstr;
+
+ rc = rwm_map_attrnames( op, &rwmap->rwm_at, &rwmap->rwm_oc,
+ op->ors_attrs, &an, RWM_MAP );
+ if ( rc != LDAP_SUCCESS ) {
+ text = "attribute list mapping error";
+ goto error_return;
+ }
+
+ op->ors_attrs = an;
+ /* store the mapped Attributes for later usage, in
+ * the case that other overlays change op->ors_attrs */
+ roc->ros.mapped_attrs = an;
+ roc->cb.sc_response = rwm_swap_attrs;
+
+ op->o_callback = &roc->cb;
+
+ return SLAP_CB_CONTINUE;
+
+error_return:;
+ if ( an != NULL ) {
+ ch_free( an );
+ }
+
+ if ( f != NULL ) {
+ filter_free_x( op, f, 1 );
+ }
+
+ if ( !BER_BVISNULL( &fstr ) ) {
+ op->o_tmpfree( fstr.bv_val, op->o_tmpmemctx );
+ }
+
+ rwm_op_rollback( op, rs, &roc->ros );
+ op->oq_search = roc->ros.oq_search;
+ op->o_tmpfree( roc, op->o_tmpmemctx );
+
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+ send_ldap_error( op, rs, rc, text );
+
+ return -1;
+
+}
+
+static int
+rwm_exop_passwd( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ int rc;
+ rwm_op_cb *roc;
+
+ struct berval id = BER_BVNULL,
+ pwold = BER_BVNULL,
+ pwnew = BER_BVNULL;
+ BerElement *ber = NULL;
+
+ if ( !BER_BVISNULL( &op->o_req_ndn ) ) {
+ return LDAP_SUCCESS;
+ }
+
+ if ( !SLAP_ISGLOBALOVERLAY( op->o_bd ) ) {
+ rs->sr_err = LDAP_OTHER;
+ return rs->sr_err;
+ }
+
+ rs->sr_err = slap_passwd_parse( op->ore_reqdata, &id,
+ &pwold, &pwnew, &rs->sr_text );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ return rs->sr_err;
+ }
+
+ if ( !BER_BVISNULL( &id ) ) {
+ char idNul = id.bv_val[id.bv_len];
+ id.bv_val[id.bv_len] = '\0';
+ rs->sr_err = dnPrettyNormal( NULL, &id, &op->o_req_dn,
+ &op->o_req_ndn, op->o_tmpmemctx );
+ id.bv_val[id.bv_len] = idNul;
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ rs->sr_text = "Invalid DN";
+ return rs->sr_err;
+ }
+
+ } else {
+ ber_dupbv_x( &op->o_req_dn, &op->o_dn, op->o_tmpmemctx );
+ ber_dupbv_x( &op->o_req_ndn, &op->o_ndn, op->o_tmpmemctx );
+ }
+
+ roc = rwm_callback_get( op, rs );
+
+ rc = rwm_op_dn_massage( op, rs, "extendedDN", &roc->ros );
+ if ( rc != LDAP_SUCCESS ) {
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+ send_ldap_error( op, rs, rc, "extendedDN massage error" );
+ return -1;
+ }
+
+ ber = ber_alloc_t( LBER_USE_DER );
+ if ( !ber ) {
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "No memory";
+ return rs->sr_err;
+ }
+ ber_printf( ber, "{" );
+ if ( !BER_BVISNULL( &id )) {
+ ber_printf( ber, "tO", LDAP_TAG_EXOP_MODIFY_PASSWD_ID,
+ &op->o_req_dn );
+ }
+ if ( !BER_BVISNULL( &pwold )) {
+ ber_printf( ber, "tO", LDAP_TAG_EXOP_MODIFY_PASSWD_OLD, &pwold );
+ }
+ if ( !BER_BVISNULL( &pwnew )) {
+ ber_printf( ber, "tO", LDAP_TAG_EXOP_MODIFY_PASSWD_NEW, &pwnew );
+ }
+ ber_printf( ber, "N}" );
+ ber_flatten( ber, &op->ore_reqdata );
+ ber_free( ber, 1 );
+
+ op->o_callback = &roc->cb;
+
+ return SLAP_CB_CONTINUE;
+}
+
+static struct exop {
+ struct berval oid;
+ BI_op_extended *extended;
+} exop_table[] = {
+ { BER_BVC(LDAP_EXOP_MODIFY_PASSWD), rwm_exop_passwd },
+ { BER_BVNULL, NULL }
+};
+
+static int
+rwm_extended( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ int rc;
+ rwm_op_cb *roc;
+
+ int i;
+
+ for ( i = 0; exop_table[i].extended != NULL; i++ ) {
+ if ( bvmatch( &exop_table[i].oid, &op->oq_extended.rs_reqoid ) )
+ {
+ rc = exop_table[i].extended( op, rs );
+ switch ( rc ) {
+ case LDAP_SUCCESS:
+ break;
+
+ case SLAP_CB_CONTINUE:
+ case SLAPD_ABANDON:
+ return rc;
+
+ default:
+ send_ldap_result( op, rs );
+ return rc;
+ }
+ break;
+ }
+ }
+
+ roc = rwm_callback_get( op, rs );
+
+ rc = rwm_op_dn_massage( op, rs, "extendedDN", &roc->ros );
+ if ( rc != LDAP_SUCCESS ) {
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+ send_ldap_error( op, rs, rc, "extendedDN massage error" );
+ return -1;
+ }
+
+ /* TODO: rewrite/map extended data ? ... */
+ op->o_callback = &roc->cb;
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+rwm_matched( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ struct ldaprwmap *rwmap =
+ (struct ldaprwmap *)on->on_bi.bi_private;
+
+ struct berval dn, mdn;
+ dncookie dc;
+ int rc;
+
+ if ( rs->sr_matched == NULL ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ dc.rwmap = rwmap;
+ dc.conn = op->o_conn;
+ dc.rs = rs;
+ dc.ctx = "matchedDN";
+ ber_str2bv( rs->sr_matched, 0, 0, &dn );
+ mdn = dn;
+ rc = rwm_dn_massage_pretty( &dc, &dn, &mdn );
+ if ( rc != LDAP_SUCCESS ) {
+ rs->sr_err = rc;
+ rs->sr_text = "Rewrite error";
+ return 1;
+ }
+
+ if ( mdn.bv_val != dn.bv_val ) {
+ if ( rs->sr_flags & REP_MATCHED_MUSTBEFREED ) {
+ ch_free( (void *)rs->sr_matched );
+
+ } else {
+ rs->sr_flags |= REP_MATCHED_MUSTBEFREED;
+ }
+ rs->sr_matched = mdn.bv_val;
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+rwm_attrs( Operation *op, SlapReply *rs, Attribute** a_first, int stripEntryDN )
+{
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ struct ldaprwmap *rwmap =
+ (struct ldaprwmap *)on->on_bi.bi_private;
+
+ dncookie dc;
+ int rc;
+ Attribute **ap;
+ int isupdate;
+ int check_duplicate_attrs = 0;
+
+ /*
+ * Rewrite the dn attrs, if needed
+ */
+ dc.rwmap = rwmap;
+ dc.conn = op->o_conn;
+ dc.rs = NULL;
+
+ /* FIXME: the entries are in the remote mapping form;
+ * so we need to select those attributes we are willing
+ * to return, and remap them accordingly */
+
+ /* FIXME: in principle, one could map an attribute
+ * on top of another, which already exists.
+ * As such, in the end there might exist more than
+ * one instance of an attribute.
+ * We should at least check if this occurs, and issue
+ * an error (because multiple instances of attrs in
+ * response are not valid), or merge the values (what
+ * about duplicate values?) */
+ isupdate = be_shadow_update( op );
+ for ( ap = a_first; *ap; ) {
+ struct ldapmapping *mapping = NULL;
+ int drop_missing;
+ int last = -1;
+ Attribute *a;
+
+ if ( ( rwmap->rwm_flags & RWM_F_DROP_UNREQUESTED_ATTRS ) &&
+ op->ors_attrs != NULL &&
+ !SLAP_USERATTRS( rs->sr_attr_flags ) &&
+ !ad_inlist( (*ap)->a_desc, op->ors_attrs ) )
+ {
+ goto cleanup_attr;
+ }
+
+ drop_missing = rwm_mapping( &rwmap->rwm_at,
+ &(*ap)->a_desc->ad_cname, &mapping, RWM_REMAP );
+ if ( drop_missing || ( mapping != NULL && BER_BVISEMPTY( &mapping->m_dst ) ) )
+ {
+ goto cleanup_attr;
+ }
+ if ( mapping != NULL ) {
+ assert( mapping->m_dst_ad != NULL );
+
+ /* try to normalize mapped Attributes if the original
+ * AttributeType was not normalized */
+ if ( (!(*ap)->a_desc->ad_type->sat_equality ||
+ !(*ap)->a_desc->ad_type->sat_equality->smr_normalize) &&
+ mapping->m_dst_ad->ad_type->sat_equality &&
+ mapping->m_dst_ad->ad_type->sat_equality->smr_normalize )
+ {
+ if ((rwmap->rwm_flags & RWM_F_NORMALIZE_MAPPED_ATTRS))
+ {
+ int i = 0;
+
+ last = (*ap)->a_numvals;
+ if ( last )
+ {
+ (*ap)->a_nvals = ch_malloc( (last+1) * sizeof(struct berval) );
+
+ for ( i = 0; !BER_BVISNULL( &(*ap)->a_vals[i]); i++ ) {
+ int rc;
+ /*
+ * check that each value is valid per syntax
+ * and pretty if appropriate
+ */
+ rc = mapping->m_dst_ad->ad_type->sat_equality->smr_normalize(
+ SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX,
+ mapping->m_dst_ad->ad_type->sat_syntax,
+ mapping->m_dst_ad->ad_type->sat_equality,
+ &(*ap)->a_vals[i], &(*ap)->a_nvals[i],
+ NULL );
+
+ if ( rc != LDAP_SUCCESS ) {
+ BER_BVZERO( &(*ap)->a_nvals[i] );
+ }
+ }
+ BER_BVZERO( &(*ap)->a_nvals[i] );
+ }
+
+ } else {
+ assert( (*ap)->a_nvals == (*ap)->a_vals );
+ (*ap)->a_nvals = NULL;
+ ber_bvarray_dup_x( &(*ap)->a_nvals, (*ap)->a_vals, NULL );
+ }
+ }
+
+ /* rewrite the attribute description */
+ (*ap)->a_desc = mapping->m_dst_ad;
+
+ /* will need to check for duplicate attrs */
+ check_duplicate_attrs++;
+ }
+
+ if ( (*ap)->a_desc == slap_schema.si_ad_entryDN ) {
+ if ( stripEntryDN ) {
+ /* will be generated by frontend */
+ goto cleanup_attr;
+ }
+
+ } else if ( !isupdate
+ && !get_relax( op )
+ && (*ap)->a_desc->ad_type->sat_no_user_mod
+ && (*ap)->a_desc->ad_type != slap_schema.si_at_undefined )
+ {
+ goto next_attr;
+ }
+
+ if ( last == -1 ) { /* not yet counted */
+ last = (*ap)->a_numvals;
+ }
+
+ if ( last == 0 ) {
+ /* empty? leave it in place because of attrsonly and vlv */
+ goto next_attr;
+ }
+ last--;
+
+ if ( (*ap)->a_desc == slap_schema.si_ad_objectClass
+ || (*ap)->a_desc == slap_schema.si_ad_structuralObjectClass )
+ {
+ struct berval *bv;
+
+ for ( bv = (*ap)->a_vals; !BER_BVISNULL( bv ); bv++ ) {
+ struct berval mapped;
+
+ rwm_map( &rwmap->rwm_oc, &bv[0], &mapped, RWM_REMAP );
+ if ( BER_BVISNULL( &mapped ) || BER_BVISEMPTY( &mapped ) ) {
+remove_oc:;
+ ch_free( bv[0].bv_val );
+ BER_BVZERO( &bv[0] );
+ if ( &(*ap)->a_vals[last] > &bv[0] ) {
+ bv[0] = (*ap)->a_vals[last];
+ BER_BVZERO( &(*ap)->a_vals[last] );
+ }
+ last--;
+ bv--;
+
+ } else if ( mapped.bv_val != bv[0].bv_val
+ && ber_bvstrcasecmp( &mapped, &bv[0] ) != 0 )
+ {
+ int i;
+
+ for ( i = 0; !BER_BVISNULL( &(*ap)->a_vals[ i ] ); i++ ) {
+ if ( &(*ap)->a_vals[ i ] == bv ) {
+ continue;
+ }
+
+ if ( ber_bvstrcasecmp( &mapped, &(*ap)->a_vals[ i ] ) == 0 ) {
+ break;
+ }
+ }
+
+ if ( !BER_BVISNULL( &(*ap)->a_vals[ i ] ) ) {
+ goto remove_oc;
+ }
+
+ /*
+ * FIXME: after LBER_FREEing
+ * the value is replaced by
+ * ch_alloc'ed memory
+ */
+ ber_bvreplace( &bv[0], &mapped );
+
+ /* FIXME: will need to check
+ * if the structuralObjectClass
+ * changed */
+ }
+ }
+
+ /*
+ * It is necessary to try to rewrite attributes with
+ * dn syntax because they might be used in ACLs as
+ * members of groups; since ACLs are applied to the
+ * rewritten stuff, no dn-based subject clause could
+ * be used at the ldap backend side (see
+ * http://www.OpenLDAP.org/faq/data/cache/452.html)
+ * The problem can be overcome by moving the dn-based
+ * ACLs to the target directory server, and letting
+ * everything pass thru the ldap backend. */
+ /* FIXME: handle distinguishedName-like syntaxes, like
+ * nameAndOptionalUID */
+ } else if ( (*ap)->a_desc->ad_type->sat_syntax == slap_schema.si_syn_distinguishedName
+ || ( mapping != NULL && mapping->m_src_ad->ad_type->sat_syntax == slap_schema.si_syn_distinguishedName ) )
+ {
+ dc.ctx = "searchAttrDN";
+ rc = rwm_dnattr_result_rewrite( &dc, (*ap)->a_vals, (*ap)->a_nvals );
+ if ( rc != LDAP_SUCCESS ) {
+ goto cleanup_attr;
+ }
+
+ } else if ( (*ap)->a_desc == slap_schema.si_ad_ref ) {
+ dc.ctx = "searchAttrDN";
+ rc = rwm_referral_result_rewrite( &dc, (*ap)->a_vals );
+ if ( rc != LDAP_SUCCESS ) {
+ goto cleanup_attr;
+ }
+ }
+
+
+next_attr:;
+ ap = &(*ap)->a_next;
+ continue;
+
+cleanup_attr:;
+ a = *ap;
+ *ap = (*ap)->a_next;
+
+ attr_free( a );
+ }
+
+ /* only check if some mapping occurred */
+ if ( check_duplicate_attrs ) {
+ for ( ap = a_first; *ap != NULL; ap = &(*ap)->a_next ) {
+ Attribute **tap;
+
+ for ( tap = &(*ap)->a_next; *tap != NULL; ) {
+ if ( (*tap)->a_desc == (*ap)->a_desc ) {
+ Entry e = { 0 };
+ Modification mod = { 0 };
+ const char *text = NULL;
+ char textbuf[ SLAP_TEXT_BUFLEN ];
+ Attribute *next = (*tap)->a_next;
+
+ BER_BVSTR( &e.e_name, "" );
+ BER_BVSTR( &e.e_nname, "" );
+ e.e_attrs = *ap;
+ mod.sm_op = LDAP_MOD_ADD;
+ mod.sm_desc = (*ap)->a_desc;
+ mod.sm_type = mod.sm_desc->ad_cname;
+ mod.sm_numvals = (*tap)->a_numvals;
+ mod.sm_values = (*tap)->a_vals;
+ if ( (*tap)->a_nvals != (*tap)->a_vals ) {
+ mod.sm_nvalues = (*tap)->a_nvals;
+ }
+
+ (void)modify_add_values( &e, &mod,
+ /* permissive */ 1,
+ &text, textbuf, sizeof( textbuf ) );
+
+ /* should not insert new attrs! */
+ assert( e.e_attrs == *ap );
+
+ attr_free( *tap );
+ *tap = next;
+
+ } else {
+ tap = &(*tap)->a_next;
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int
+rwm_send_entry( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ struct ldaprwmap *rwmap =
+ (struct ldaprwmap *)on->on_bi.bi_private;
+
+ Entry *e = NULL;
+ slap_mask_t flags;
+ struct berval dn = BER_BVNULL,
+ ndn = BER_BVNULL;
+ dncookie dc;
+ int rc;
+
+ assert( rs->sr_entry != NULL );
+
+ /*
+ * Rewrite the dn of the result, if needed
+ */
+ dc.rwmap = rwmap;
+ dc.conn = op->o_conn;
+ dc.rs = NULL;
+ dc.ctx = "searchEntryDN";
+
+ e = rs->sr_entry;
+ flags = rs->sr_flags;
+ if ( !( rs->sr_flags & REP_ENTRY_MODIFIABLE ) ) {
+ /* FIXME: all we need to duplicate are:
+ * - dn
+ * - ndn
+ * - attributes that are requested
+ * - no values if attrsonly is set
+ */
+
+ e = entry_dup( e );
+ if ( e == NULL ) {
+ rc = LDAP_NO_MEMORY;
+ goto fail;
+ }
+
+ flags &= ~REP_ENTRY_MUSTRELEASE;
+ flags |= ( REP_ENTRY_MODIFIABLE | REP_ENTRY_MUSTBEFREED );
+ }
+
+ /*
+ * Note: this may fail if the target host(s) schema differs
+ * from the one known to the meta, and a DN with unknown
+ * attributes is returned.
+ */
+ dn = e->e_name;
+ ndn = e->e_nname;
+ rc = rwm_dn_massage_pretty_normalize( &dc, &e->e_name, &dn, &ndn );
+ if ( rc != LDAP_SUCCESS ) {
+ rc = 1;
+ goto fail;
+ }
+
+ if ( e->e_name.bv_val != dn.bv_val ) {
+ ch_free( e->e_name.bv_val );
+ ch_free( e->e_nname.bv_val );
+
+ e->e_name = dn;
+ e->e_nname = ndn;
+ }
+
+ /* TODO: map entry attribute types, objectclasses
+ * and dn-valued attribute values */
+
+ /* FIXME: the entries are in the remote mapping form;
+ * so we need to select those attributes we are willing
+ * to return, and remap them accordingly */
+ (void)rwm_attrs( op, rs, &e->e_attrs, 1 );
+
+ if ( rs->sr_flags & REP_ENTRY_MUSTRELEASE ) {
+ /* ITS#6423: REP_ENTRY_MUSTRELEASE incompatible
+ * with REP_ENTRY_MODIFIABLE */
+ if ( rs->sr_entry == e ) {
+ rc = 1;
+ goto fail;
+ }
+
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+ be_entry_release_r( op, rs->sr_entry );
+ op->o_bd->bd_info = (BackendInfo *)on;
+ }
+
+ rs->sr_entry = e;
+ rs->sr_flags = flags;
+
+ return SLAP_CB_CONTINUE;
+
+fail:;
+ if ( e != NULL && e != rs->sr_entry ) {
+ if ( e->e_name.bv_val == dn.bv_val ) {
+ BER_BVZERO( &e->e_name );
+ }
+
+ if ( e->e_nname.bv_val == ndn.bv_val ) {
+ BER_BVZERO( &e->e_nname );
+ }
+
+ entry_free( e );
+ }
+
+ if ( !BER_BVISNULL( &dn ) ) {
+ ch_free( dn.bv_val );
+ }
+
+ if ( !BER_BVISNULL( &ndn ) ) {
+ ch_free( ndn.bv_val );
+ }
+
+ return rc;
+}
+
+static int
+rwm_operational( Operation *op, SlapReply *rs )
+{
+ /* FIXME: the entries are in the remote mapping form;
+ * so we need to select those attributes we are willing
+ * to return, and remap them accordingly */
+ if ( rs->sr_operational_attrs ) {
+ rwm_attrs( op, rs, &rs->sr_operational_attrs, 1 );
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+#if 0
+/* don't use this; it cannot be reverted, and leaves op->o_req_dn
+ * rewritten for subsequent operations; fine for plain suffixmassage,
+ * but destroys everything else */
+static int
+rwm_chk_referrals( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ int rc;
+
+ rc = rwm_op_dn_massage( op, rs, "referralCheckDN" );
+ if ( rc != LDAP_SUCCESS ) {
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+ send_ldap_error( op, rs, rc, "referralCheckDN massage error" );
+ return -1;
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+#endif
+
+static int
+rwm_rw_config(
+ BackendDB *be,
+ const char *fname,
+ int lineno,
+ int argc,
+ char **argv )
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+ struct ldaprwmap *rwmap =
+ (struct ldaprwmap *)on->on_bi.bi_private;
+
+ return rewrite_parse( rwmap->rwm_rw,
+ fname, lineno, argc, argv );
+
+ return 0;
+}
+
+static int
+rwm_suffixmassage_config(
+ BackendDB *be,
+ const char *fname,
+ int lineno,
+ int argc,
+ char **argv )
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+ struct ldaprwmap *rwmap =
+ (struct ldaprwmap *)on->on_bi.bi_private;
+
+ struct berval bvnc, nvnc, pvnc, brnc, nrnc, prnc;
+ int massaged;
+ int rc;
+
+ /*
+ * syntax:
+ *
+ * suffixmassage [<suffix>] <massaged suffix>
+ *
+ * the [<suffix>] field must be defined as a valid suffix
+ * for the current database;
+ * the <massaged suffix> shouldn't have already been
+ * defined as a valid suffix for the current server
+ */
+ if ( argc == 2 ) {
+ if ( be->be_suffix == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "%s: line %d: "
+ " \"suffixMassage [<suffix>]"
+ " <massaged suffix>\" without "
+ "<suffix> part requires database "
+ "suffix be defined first.\n",
+ fname, lineno, 0 );
+ return 1;
+ }
+ bvnc = be->be_suffix[ 0 ];
+ massaged = 1;
+
+ } else if ( argc == 3 ) {
+ ber_str2bv( argv[ 1 ], 0, 0, &bvnc );
+ massaged = 2;
+
+ } else {
+ Debug( LDAP_DEBUG_ANY, "%s: line %d: syntax is"
+ " \"suffixMassage [<suffix>]"
+ " <massaged suffix>\"\n",
+ fname, lineno, 0 );
+ return 1;
+ }
+
+ if ( dnPrettyNormal( NULL, &bvnc, &pvnc, &nvnc, NULL ) != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "%s: line %d: suffix DN %s is invalid\n",
+ fname, lineno, bvnc.bv_val );
+ return 1;
+ }
+
+ ber_str2bv( argv[ massaged ], 0, 0, &brnc );
+ if ( dnPrettyNormal( NULL, &brnc, &prnc, &nrnc, NULL ) != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "%s: line %d: suffix DN %s is invalid\n",
+ fname, lineno, brnc.bv_val );
+ free( nvnc.bv_val );
+ free( pvnc.bv_val );
+ return 1;
+ }
+
+ /*
+ * The suffix massaging is emulated
+ * by means of the rewrite capabilities
+ */
+ rc = rwm_suffix_massage_config( rwmap->rwm_rw,
+ &pvnc, &nvnc, &prnc, &nrnc );
+ free( nvnc.bv_val );
+ free( pvnc.bv_val );
+ free( nrnc.bv_val );
+ free( prnc.bv_val );
+
+ return rc;
+}
+
+static int
+rwm_m_config(
+ BackendDB *be,
+ const char *fname,
+ int lineno,
+ int argc,
+ char **argv )
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+ struct ldaprwmap *rwmap =
+ (struct ldaprwmap *)on->on_bi.bi_private;
+
+ /* objectclass/attribute mapping */
+ return rwm_map_config( &rwmap->rwm_oc,
+ &rwmap->rwm_at,
+ fname, lineno, argc, argv );
+}
+
+static int
+rwm_response( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ struct ldaprwmap *rwmap =
+ (struct ldaprwmap *)on->on_bi.bi_private;
+
+ int rc;
+
+ if ( op->o_tag == LDAP_REQ_SEARCH && rs->sr_type == REP_SEARCH ) {
+ return rwm_send_entry( op, rs );
+ }
+
+ switch( op->o_tag ) {
+ case LDAP_REQ_SEARCH:
+ case LDAP_REQ_BIND:
+ case LDAP_REQ_ADD:
+ case LDAP_REQ_DELETE:
+ case LDAP_REQ_MODRDN:
+ case LDAP_REQ_MODIFY:
+ case LDAP_REQ_COMPARE:
+ case LDAP_REQ_EXTENDED:
+ if ( rs->sr_ref ) {
+ dncookie dc;
+
+ /*
+ * Rewrite the dn of the referrals, if needed
+ */
+ dc.rwmap = rwmap;
+ dc.conn = op->o_conn;
+ dc.rs = NULL;
+ dc.ctx = "referralDN";
+ rc = rwm_referral_result_rewrite( &dc, rs->sr_ref );
+ if ( rc != LDAP_SUCCESS ) {
+ rc = 1;
+ break;
+ }
+ }
+ rc = rwm_matched( op, rs );
+ break;
+
+ default:
+ rc = SLAP_CB_CONTINUE;
+ break;
+ }
+
+ return rc;
+}
+
+static int
+rwm_db_config(
+ BackendDB *be,
+ const char *fname,
+ int lineno,
+ int argc,
+ char **argv )
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+ struct ldaprwmap *rwmap =
+ (struct ldaprwmap *)on->on_bi.bi_private;
+
+ int rc = 0;
+ char *argv0 = NULL;
+
+ if ( strncasecmp( argv[ 0 ], "rwm-", STRLENOF( "rwm-" ) ) == 0 ) {
+ argv0 = argv[ 0 ];
+ argv[ 0 ] = &argv0[ STRLENOF( "rwm-" ) ];
+ }
+
+ if ( strncasecmp( argv[0], "rewrite", STRLENOF("rewrite") ) == 0 ) {
+ rc = rwm_rw_config( be, fname, lineno, argc, argv );
+
+ } else if ( strcasecmp( argv[0], "map" ) == 0 ) {
+ rc = rwm_m_config( be, fname, lineno, argc, argv );
+
+ } else if ( strcasecmp( argv[0], "suffixmassage" ) == 0 ) {
+ rc = rwm_suffixmassage_config( be, fname, lineno, argc, argv );
+
+ } else if ( strcasecmp( argv[0], "t-f-support" ) == 0 ) {
+ if ( argc != 2 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: \"t-f-support {no|yes|discover}\" needs 1 argument.\n",
+ fname, lineno, 0 );
+ return( 1 );
+ }
+
+ if ( strcasecmp( argv[ 1 ], "no" ) == 0 ) {
+ rwmap->rwm_flags &= ~(RWM_F_SUPPORT_T_F_MASK2);
+
+ } else if ( strcasecmp( argv[ 1 ], "yes" ) == 0 ) {
+ rwmap->rwm_flags |= RWM_F_SUPPORT_T_F;
+
+ /* TODO: not implemented yet */
+ } else if ( strcasecmp( argv[ 1 ], "discover" ) == 0 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: \"discover\" not supported yet "
+ "in \"t-f-support {no|yes|discover}\".\n",
+ fname, lineno, 0 );
+ return( 1 );
+#if 0
+ rwmap->rwm_flags |= RWM_F_SUPPORT_T_F_DISCOVER;
+#endif
+
+ } else {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: unknown value \"%s\" for \"t-f-support {no|yes|discover}\".\n",
+ fname, lineno, argv[ 1 ] );
+ return 1;
+ }
+
+ } else if ( strcasecmp( argv[0], "normalize-mapped-attrs" ) == 0 ) {
+ if ( argc !=2 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: \"normalize-mapped-attrs {no|yes}\" needs 1 argument.\n",
+ fname, lineno, 0 );
+ return( 1 );
+ }
+
+ if ( strcasecmp( argv[ 1 ], "no" ) == 0 ) {
+ rwmap->rwm_flags &= ~(RWM_F_NORMALIZE_MAPPED_ATTRS);
+
+ } else if ( strcasecmp( argv[ 1 ], "yes" ) == 0 ) {
+ rwmap->rwm_flags |= RWM_F_NORMALIZE_MAPPED_ATTRS;
+ }
+
+ } else {
+ rc = SLAP_CONF_UNKNOWN;
+ }
+
+ if ( argv0 ) {
+ argv[ 0 ] = argv0;
+ }
+
+ return rc;
+}
+
+/*
+ * dynamic configuration...
+ */
+
+enum {
+ /* rewrite */
+ RWM_CF_REWRITE = 1,
+
+ /* map */
+ RWM_CF_MAP,
+ RWM_CF_T_F_SUPPORT,
+ RWM_CF_NORMALIZE_MAPPED,
+ RWM_CF_DROP_UNREQUESTED,
+
+ RWM_CF_LAST
+};
+
+static slap_verbmasks t_f_mode[] = {
+ { BER_BVC( "true" ), RWM_F_SUPPORT_T_F },
+ { BER_BVC( "yes" ), RWM_F_SUPPORT_T_F },
+ { BER_BVC( "discover" ), RWM_F_SUPPORT_T_F_DISCOVER },
+ { BER_BVC( "false" ), RWM_F_NONE },
+ { BER_BVC( "no" ), RWM_F_NONE },
+ { BER_BVNULL, 0 }
+};
+
+static ConfigDriver rwm_cf_gen;
+
+static ConfigTable rwmcfg[] = {
+ { "rwm-rewrite", "rewrite",
+ 2, 0, STRLENOF("rwm-rewrite"),
+ ARG_MAGIC|RWM_CF_REWRITE, rwm_cf_gen,
+ "( OLcfgOvAt:16.1 NAME 'olcRwmRewrite' "
+ "DESC 'Rewrites strings' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString "
+ "X-ORDERED 'VALUES' )",
+ NULL, NULL },
+
+ { "rwm-suffixmassage", "[virtual]> <real",
+ 2, 3, 0, ARG_MAGIC|RWM_CF_REWRITE, rwm_cf_gen,
+ NULL, NULL, NULL },
+
+ { "rwm-t-f-support", "true|false|discover",
+ 2, 2, 0, ARG_MAGIC|RWM_CF_T_F_SUPPORT, rwm_cf_gen,
+ "( OLcfgOvAt:16.2 NAME 'olcRwmTFSupport' "
+ "DESC 'Absolute filters support' "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+
+ { "rwm-map", "{objectClass|attribute}",
+ 2, 4, 0, ARG_MAGIC|RWM_CF_MAP, rwm_cf_gen,
+ "( OLcfgOvAt:16.3 NAME 'olcRwmMap' "
+ "DESC 'maps attributes/objectClasses' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString "
+ "X-ORDERED 'VALUES' )",
+ NULL, NULL },
+
+ { "rwm-normalize-mapped-attrs", "true|false",
+ 2, 2, 0, ARG_MAGIC|ARG_ON_OFF|RWM_CF_NORMALIZE_MAPPED, rwm_cf_gen,
+ "( OLcfgOvAt:16.4 NAME 'olcRwmNormalizeMapped' "
+ "DESC 'Normalize mapped attributes/objectClasses' "
+ "SYNTAX OMsBoolean "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+
+ { "rwm-drop-unrequested-attrs", "true|false",
+ 2, 2, 0, ARG_MAGIC|ARG_ON_OFF|RWM_CF_DROP_UNREQUESTED, rwm_cf_gen,
+ "( OLcfgOvAt:16.5 NAME 'olcRwmDropUnrequested' "
+ "DESC 'Drop unrequested attributes' "
+ "SYNTAX OMsBoolean "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+
+ { NULL, NULL, 0, 0, 0, ARG_IGNORED }
+};
+
+static ConfigOCs rwmocs[] = {
+ { "( OLcfgOvOc:16.1 "
+ "NAME 'olcRwmConfig' "
+ "DESC 'Rewrite/remap configuration' "
+ "SUP olcOverlayConfig "
+ "MAY ( "
+ "olcRwmRewrite $ "
+ "olcRwmTFSupport $ "
+ "olcRwmMap $ "
+ "olcRwmNormalizeMapped "
+ ") )",
+ Cft_Overlay, rwmcfg, NULL, NULL },
+ { NULL, 0, NULL }
+};
+
+static void
+slap_bv_x_ordered_unparse( BerVarray in, BerVarray *out )
+{
+ int i;
+ BerVarray bva = NULL;
+ char ibuf[32], *ptr;
+ struct berval idx;
+
+ assert( in != NULL );
+
+ for ( i = 0; !BER_BVISNULL( &in[i] ); i++ )
+ /* count'em */ ;
+
+ if ( i == 0 ) {
+ return;
+ }
+
+ idx.bv_val = ibuf;
+
+ bva = ch_malloc( ( i + 1 ) * sizeof(struct berval) );
+ BER_BVZERO( &bva[ 0 ] );
+
+ for ( i = 0; !BER_BVISNULL( &in[i] ); i++ ) {
+ idx.bv_len = snprintf( idx.bv_val, sizeof( ibuf ), "{%d}", i );
+ if ( idx.bv_len >= sizeof( ibuf ) ) {
+ ber_bvarray_free( bva );
+ return;
+ }
+
+ bva[i].bv_len = idx.bv_len + in[i].bv_len;
+ bva[i].bv_val = ch_malloc( bva[i].bv_len + 1 );
+ ptr = lutil_strcopy( bva[i].bv_val, ibuf );
+ ptr = lutil_strcopy( ptr, in[i].bv_val );
+ *ptr = '\0';
+ BER_BVZERO( &bva[ i + 1 ] );
+ }
+
+ *out = bva;
+}
+
+static int
+rwm_bva_add(
+ BerVarray *bva,
+ int idx,
+ char **argv )
+{
+ char *line;
+ struct berval bv;
+
+ line = ldap_charray2str( argv, "\" \"" );
+ if ( line != NULL ) {
+ int len = strlen( argv[ 0 ] );
+
+ ber_str2bv( line, 0, 0, &bv );
+ AC_MEMCPY( &bv.bv_val[ len ], &bv.bv_val[ len + 1 ],
+ bv.bv_len - ( len + 1 ) );
+ bv.bv_val[ bv.bv_len - 1 ] = '"';
+
+ if ( idx == -1 ) {
+ ber_bvarray_add( bva, &bv );
+
+ } else {
+ (*bva)[ idx ] = bv;
+ }
+
+ return 0;
+ }
+
+ return -1;
+}
+
+static int
+rwm_bva_rewrite_add(
+ struct ldaprwmap *rwmap,
+ int idx,
+ char **argv )
+{
+ return rwm_bva_add( &rwmap->rwm_bva_rewrite, idx, argv );
+}
+
+static int
+rwm_bva_map_add(
+ struct ldaprwmap *rwmap,
+ int idx,
+ char **argv )
+{
+ return rwm_bva_add( &rwmap->rwm_bva_map, idx, argv );
+}
+
+static int
+rwm_info_init( struct rewrite_info ** rwm_rw )
+{
+ char *rargv[ 3 ];
+
+ *rwm_rw = rewrite_info_init( REWRITE_MODE_USE_DEFAULT );
+ if ( *rwm_rw == NULL ) {
+ return -1;
+ }
+
+ /* this rewriteContext by default must be null;
+ * rules can be added if required */
+ rargv[ 0 ] = "rewriteContext";
+ rargv[ 1 ] = "searchFilter";
+ rargv[ 2 ] = NULL;
+ rewrite_parse( *rwm_rw, "<suffix massage>", 1, 2, rargv );
+
+ rargv[ 0 ] = "rewriteContext";
+ rargv[ 1 ] = "default";
+ rargv[ 2 ] = NULL;
+ rewrite_parse( *rwm_rw, "<suffix massage>", 2, 2, rargv );
+
+ return 0;
+}
+
+static int
+rwm_cf_gen( ConfigArgs *c )
+{
+ slap_overinst *on = (slap_overinst *)c->bi;
+ struct ldaprwmap *rwmap =
+ (struct ldaprwmap *)on->on_bi.bi_private;
+
+ BackendDB db;
+ char *argv0;
+ int idx0 = 0;
+ int rc = 0;
+
+ db = *c->be;
+ db.bd_info = c->bi;
+
+ if ( c->op == SLAP_CONFIG_EMIT ) {
+ struct berval bv = BER_BVNULL;
+
+ switch ( c->type ) {
+ case RWM_CF_REWRITE:
+ if ( rwmap->rwm_bva_rewrite == NULL ) {
+ rc = 1;
+
+ } else {
+ slap_bv_x_ordered_unparse( rwmap->rwm_bva_rewrite, &c->rvalue_vals );
+ if ( !c->rvalue_vals ) {
+ rc = 1;
+ }
+ }
+ break;
+
+ case RWM_CF_T_F_SUPPORT:
+ enum_to_verb( t_f_mode, (rwmap->rwm_flags & RWM_F_SUPPORT_T_F_MASK2), &bv );
+ if ( BER_BVISNULL( &bv ) ) {
+ /* there's something wrong... */
+ assert( 0 );
+ rc = 1;
+
+ } else {
+ value_add_one( &c->rvalue_vals, &bv );
+ }
+ break;
+
+ case RWM_CF_MAP:
+ if ( rwmap->rwm_bva_map == NULL ) {
+ rc = 1;
+
+ } else {
+ slap_bv_x_ordered_unparse( rwmap->rwm_bva_map, &c->rvalue_vals );
+ if ( !c->rvalue_vals ) {
+ rc = 1;
+ }
+ }
+ break;
+
+ case RWM_CF_NORMALIZE_MAPPED:
+ c->value_int = ( rwmap->rwm_flags & RWM_F_NORMALIZE_MAPPED_ATTRS );
+ break;
+
+ case RWM_CF_DROP_UNREQUESTED:
+ c->value_int = ( rwmap->rwm_flags & RWM_F_DROP_UNREQUESTED_ATTRS );
+ break;
+
+ default:
+ assert( 0 );
+ rc = 1;
+ }
+
+ return rc;
+
+ } else if ( c->op == LDAP_MOD_DELETE ) {
+ switch ( c->type ) {
+ case RWM_CF_REWRITE:
+ if ( c->valx >= 0 ) {
+ int i;
+
+ for ( i = 0; !BER_BVISNULL( &rwmap->rwm_bva_rewrite[ i ] ); i++ )
+ /* count'em */ ;
+
+ if ( c->valx >= i ) {
+ rc = 1;
+ break;
+ }
+
+ ber_memfree( rwmap->rwm_bva_rewrite[ c->valx ].bv_val );
+ for ( i = c->valx; !BER_BVISNULL( &rwmap->rwm_bva_rewrite[ i + 1 ] ); i++ )
+ {
+ rwmap->rwm_bva_rewrite[ i ] = rwmap->rwm_bva_rewrite[ i + 1 ];
+ }
+ BER_BVZERO( &rwmap->rwm_bva_rewrite[ i ] );
+
+ rewrite_info_delete( &rwmap->rwm_rw );
+ assert( rwmap->rwm_rw == NULL );
+
+ rc = rwm_info_init( &rwmap->rwm_rw );
+
+ for ( i = 0; !BER_BVISNULL( &rwmap->rwm_bva_rewrite[ i ] ); i++ )
+ {
+ ConfigArgs ca = { 0 };
+
+ ca.line = rwmap->rwm_bva_rewrite[ i ].bv_val;
+ ca.argc = 0;
+ config_fp_parse_line( &ca );
+
+ if ( strcasecmp( ca.argv[ 0 ], "suffixmassage" ) == 0 ) {
+ rc = rwm_suffixmassage_config( &db, c->fname, c->lineno,
+ ca.argc, ca.argv );
+
+ } else {
+ rc = rwm_rw_config( &db, c->fname, c->lineno,
+ ca.argc, ca.argv );
+ }
+
+ ch_free( ca.tline );
+ ch_free( ca.argv );
+
+ assert( rc == 0 );
+ }
+
+ } else if ( rwmap->rwm_rw != NULL ) {
+ rewrite_info_delete( &rwmap->rwm_rw );
+ assert( rwmap->rwm_rw == NULL );
+
+ ber_bvarray_free( rwmap->rwm_bva_rewrite );
+ rwmap->rwm_bva_rewrite = NULL;
+
+ rc = rwm_info_init( &rwmap->rwm_rw );
+ }
+ break;
+
+ case RWM_CF_T_F_SUPPORT:
+ rwmap->rwm_flags &= ~RWM_F_SUPPORT_T_F_MASK2;
+ break;
+
+ case RWM_CF_MAP:
+ if ( c->valx >= 0 ) {
+ struct ldapmap rwm_oc = rwmap->rwm_oc;
+ struct ldapmap rwm_at = rwmap->rwm_at;
+ char *argv[5];
+ int cnt = 0;
+
+ if ( rwmap->rwm_bva_map ) {
+ for ( ; !BER_BVISNULL( &rwmap->rwm_bva_map[ cnt ] ); cnt++ )
+ /* count */ ;
+ }
+
+ if ( c->valx >= cnt ) {
+ rc = 1;
+ break;
+ }
+
+ memset( &rwmap->rwm_oc, 0, sizeof( rwmap->rwm_oc ) );
+ memset( &rwmap->rwm_at, 0, sizeof( rwmap->rwm_at ) );
+
+ /* re-parse all mappings except the one
+ * that needs to be eliminated */
+ argv[0] = "map";
+ for ( cnt = 0; !BER_BVISNULL( &rwmap->rwm_bva_map[ cnt ] ); cnt++ ) {
+ ConfigArgs ca = { 0 };
+
+ if ( cnt == c->valx ) {
+ continue;
+ }
+
+ ca.line = rwmap->rwm_bva_map[ cnt ].bv_val;
+ ca.argc = 0;
+ config_fp_parse_line( &ca );
+
+ argv[1] = ca.argv[0];
+ argv[2] = ca.argv[1];
+ argv[3] = ca.argv[2];
+ argv[4] = ca.argv[3];
+
+ rc = rwm_m_config( &db, c->fname, c->lineno, ca.argc + 1, argv );
+
+ ch_free( ca.tline );
+ ch_free( ca.argv );
+
+ /* in case of failure, restore
+ * the existing mapping */
+ if ( rc ) {
+ avl_free( rwmap->rwm_oc.remap, rwm_mapping_dst_free );
+ avl_free( rwmap->rwm_oc.map, rwm_mapping_free );
+ avl_free( rwmap->rwm_at.remap, rwm_mapping_dst_free );
+ avl_free( rwmap->rwm_at.map, rwm_mapping_free );
+ rwmap->rwm_oc = rwm_oc;
+ rwmap->rwm_at = rwm_at;
+ break;
+ }
+ }
+
+ /* in case of success, destroy the old mapping
+ * and eliminate the deleted one */
+ if ( rc == 0 ) {
+ avl_free( rwm_oc.remap, rwm_mapping_dst_free );
+ avl_free( rwm_oc.map, rwm_mapping_free );
+ avl_free( rwm_at.remap, rwm_mapping_dst_free );
+ avl_free( rwm_at.map, rwm_mapping_free );
+
+ ber_memfree( rwmap->rwm_bva_map[ c->valx ].bv_val );
+ for ( cnt = c->valx; !BER_BVISNULL( &rwmap->rwm_bva_map[ cnt ] ); cnt++ ) {
+ rwmap->rwm_bva_map[ cnt ] = rwmap->rwm_bva_map[ cnt + 1 ];
+ }
+ }
+
+ } else {
+ avl_free( rwmap->rwm_oc.remap, rwm_mapping_dst_free );
+ avl_free( rwmap->rwm_oc.map, rwm_mapping_free );
+ avl_free( rwmap->rwm_at.remap, rwm_mapping_dst_free );
+ avl_free( rwmap->rwm_at.map, rwm_mapping_free );
+
+ rwmap->rwm_oc.remap = NULL;
+ rwmap->rwm_oc.map = NULL;
+ rwmap->rwm_at.remap = NULL;
+ rwmap->rwm_at.map = NULL;
+
+ ber_bvarray_free( rwmap->rwm_bva_map );
+ rwmap->rwm_bva_map = NULL;
+ }
+ break;
+
+ case RWM_CF_NORMALIZE_MAPPED:
+ rwmap->rwm_flags &= ~RWM_F_NORMALIZE_MAPPED_ATTRS;
+ break;
+
+ case RWM_CF_DROP_UNREQUESTED:
+ rwmap->rwm_flags &= ~RWM_F_DROP_UNREQUESTED_ATTRS;
+ break;
+
+ default:
+ return 1;
+ }
+ return rc;
+ }
+
+ if ( strncasecmp( c->argv[ 0 ], "olcRwm", STRLENOF( "olcRwm" ) ) == 0 ) {
+ idx0 = 1;
+ }
+
+ switch ( c->type ) {
+ case RWM_CF_REWRITE:
+ if ( c->valx >= 0 ) {
+ struct rewrite_info *rwm_rw = rwmap->rwm_rw;
+ int i, last;
+
+ for ( last = 0; rwmap->rwm_bva_rewrite && !BER_BVISNULL( &rwmap->rwm_bva_rewrite[ last ] ); last++ )
+ /* count'em */ ;
+
+ if ( c->valx > last ) {
+ c->valx = last;
+ }
+
+ rwmap->rwm_rw = NULL;
+ rc = rwm_info_init( &rwmap->rwm_rw );
+
+ for ( i = 0; i < c->valx; i++ ) {
+ ConfigArgs ca = { 0 };
+
+ ca.line = rwmap->rwm_bva_rewrite[ i ].bv_val;
+ ca.argc = 0;
+ config_fp_parse_line( &ca );
+
+ argv0 = ca.argv[ 0 ];
+ ca.argv[ 0 ] += STRLENOF( "rwm-" );
+
+ if ( strcasecmp( ca.argv[ 0 ], "suffixmassage" ) == 0 ) {
+ rc = rwm_suffixmassage_config( &db, c->fname, c->lineno,
+ ca.argc, ca.argv );
+
+ } else {
+ rc = rwm_rw_config( &db, c->fname, c->lineno,
+ ca.argc, ca.argv );
+ }
+
+ ca.argv[ 0 ] = argv0;
+
+ ch_free( ca.tline );
+ ch_free( ca.argv );
+
+ assert( rc == 0 );
+ }
+
+ argv0 = c->argv[ idx0 ];
+ if ( strncasecmp( argv0, "rwm-", STRLENOF( "rwm-" ) ) != 0 ) {
+ return 1;
+ }
+ c->argv[ idx0 ] += STRLENOF( "rwm-" );
+ if ( strcasecmp( c->argv[ idx0 ], "suffixmassage" ) == 0 ) {
+ rc = rwm_suffixmassage_config( &db, c->fname, c->lineno,
+ c->argc - idx0, &c->argv[ idx0 ] );
+
+ } else {
+ rc = rwm_rw_config( &db, c->fname, c->lineno,
+ c->argc - idx0, &c->argv[ idx0 ] );
+ }
+ c->argv[ idx0 ] = argv0;
+ if ( rc != 0 ) {
+ rewrite_info_delete( &rwmap->rwm_rw );
+ assert( rwmap->rwm_rw == NULL );
+
+ rwmap->rwm_rw = rwm_rw;
+ return 1;
+ }
+
+ for ( i = c->valx; rwmap->rwm_bva_rewrite && !BER_BVISNULL( &rwmap->rwm_bva_rewrite[ i ] ); i++ )
+ {
+ ConfigArgs ca = { 0 };
+
+ ca.line = rwmap->rwm_bva_rewrite[ i ].bv_val;
+ ca.argc = 0;
+ config_fp_parse_line( &ca );
+
+ argv0 = ca.argv[ 0 ];
+ ca.argv[ 0 ] += STRLENOF( "rwm-" );
+
+ if ( strcasecmp( ca.argv[ 0 ], "suffixmassage" ) == 0 ) {
+ rc = rwm_suffixmassage_config( &db, c->fname, c->lineno,
+ ca.argc, ca.argv );
+
+ } else {
+ rc = rwm_rw_config( &db, c->fname, c->lineno,
+ ca.argc, ca.argv );
+ }
+
+ ca.argv[ 0 ] = argv0;
+
+ ch_free( ca.tline );
+ ch_free( ca.argv );
+
+ assert( rc == 0 );
+ }
+
+ rwmap->rwm_bva_rewrite = ch_realloc( rwmap->rwm_bva_rewrite,
+ ( last + 2 )*sizeof( struct berval ) );
+ BER_BVZERO( &rwmap->rwm_bva_rewrite[last+1] );
+
+ for ( i = last - 1; i >= c->valx; i-- )
+ {
+ rwmap->rwm_bva_rewrite[ i + 1 ] = rwmap->rwm_bva_rewrite[ i ];
+ }
+
+ rwm_bva_rewrite_add( rwmap, c->valx, &c->argv[ idx0 ] );
+
+ rewrite_info_delete( &rwm_rw );
+ assert( rwm_rw == NULL );
+
+ break;
+ }
+
+ argv0 = c->argv[ idx0 ];
+ if ( strncasecmp( argv0, "rwm-", STRLENOF( "rwm-" ) ) != 0 ) {
+ return 1;
+ }
+ c->argv[ idx0 ] += STRLENOF( "rwm-" );
+ if ( strcasecmp( c->argv[ idx0 ], "suffixmassage" ) == 0 ) {
+ rc = rwm_suffixmassage_config( &db, c->fname, c->lineno,
+ c->argc - idx0, &c->argv[ idx0 ] );
+
+ } else {
+ rc = rwm_rw_config( &db, c->fname, c->lineno,
+ c->argc - idx0, &c->argv[ idx0 ] );
+ }
+ c->argv[ idx0 ] = argv0;
+ if ( rc ) {
+ return 1;
+
+ } else {
+ rwm_bva_rewrite_add( rwmap, -1, &c->argv[ idx0 ] );
+ }
+ break;
+
+ case RWM_CF_T_F_SUPPORT:
+ rc = verb_to_mask( c->argv[ 1 ], t_f_mode );
+ if ( BER_BVISNULL( &t_f_mode[ rc ].word ) ) {
+ return 1;
+ }
+
+ rwmap->rwm_flags &= ~RWM_F_SUPPORT_T_F_MASK2;
+ rwmap->rwm_flags |= t_f_mode[ rc ].mask;
+ rc = 0;
+ break;
+
+ case RWM_CF_MAP:
+ if ( c->valx >= 0 ) {
+ struct ldapmap rwm_oc = rwmap->rwm_oc;
+ struct ldapmap rwm_at = rwmap->rwm_at;
+ char *argv[5];
+ int cnt = 0;
+
+ if ( rwmap->rwm_bva_map ) {
+ for ( ; !BER_BVISNULL( &rwmap->rwm_bva_map[ cnt ] ); cnt++ )
+ /* count */ ;
+ }
+
+ if ( c->valx >= cnt ) {
+ c->valx = cnt;
+ }
+
+ memset( &rwmap->rwm_oc, 0, sizeof( rwmap->rwm_oc ) );
+ memset( &rwmap->rwm_at, 0, sizeof( rwmap->rwm_at ) );
+
+ /* re-parse all mappings, including the one
+ * that needs to be added */
+ argv[0] = "map";
+ for ( cnt = 0; cnt < c->valx; cnt++ ) {
+ ConfigArgs ca = { 0 };
+
+ ca.line = rwmap->rwm_bva_map[ cnt ].bv_val;
+ ca.argc = 0;
+ config_fp_parse_line( &ca );
+
+ argv[1] = ca.argv[0];
+ argv[2] = ca.argv[1];
+ argv[3] = ca.argv[2];
+ argv[4] = ca.argv[3];
+
+ rc = rwm_m_config( &db, c->fname, c->lineno, ca.argc + 1, argv );
+
+ ch_free( ca.tline );
+ ch_free( ca.argv );
+
+ /* in case of failure, restore
+ * the existing mapping */
+ if ( rc ) {
+ goto rwmmap_fail;
+ }
+ }
+
+ argv0 = c->argv[0];
+ c->argv[0] = "map";
+ rc = rwm_m_config( &db, c->fname, c->lineno, c->argc, c->argv );
+ c->argv[0] = argv0;
+ if ( rc ) {
+ goto rwmmap_fail;
+ }
+
+ if ( rwmap->rwm_bva_map ) {
+ for ( ; !BER_BVISNULL( &rwmap->rwm_bva_map[ cnt ] ); cnt++ ) {
+ ConfigArgs ca = { 0 };
+
+ ca.line = rwmap->rwm_bva_map[ cnt ].bv_val;
+ ca.argc = 0;
+ config_fp_parse_line( &ca );
+
+ argv[1] = ca.argv[0];
+ argv[2] = ca.argv[1];
+ argv[3] = ca.argv[2];
+ argv[4] = ca.argv[3];
+
+ rc = rwm_m_config( &db, c->fname, c->lineno, ca.argc + 1, argv );
+
+ ch_free( ca.tline );
+ ch_free( ca.argv );
+
+ /* in case of failure, restore
+ * the existing mapping */
+ if ( rc ) {
+ goto rwmmap_fail;
+ }
+ }
+ }
+
+ /* in case of success, destroy the old mapping
+ * and add the new one */
+ if ( rc == 0 ) {
+ BerVarray tmp;
+ struct berval bv, *bvp = &bv;
+
+ if ( rwm_bva_add( &bvp, 0, &c->argv[ idx0 ] ) ) {
+ rc = 1;
+ goto rwmmap_fail;
+ }
+
+ tmp = ber_memrealloc( rwmap->rwm_bva_map,
+ sizeof( struct berval )*( cnt + 2 ) );
+ if ( tmp == NULL ) {
+ ber_memfree( bv.bv_val );
+ rc = 1;
+ goto rwmmap_fail;
+ }
+ rwmap->rwm_bva_map = tmp;
+ BER_BVZERO( &rwmap->rwm_bva_map[ cnt + 1 ] );
+
+ avl_free( rwm_oc.remap, rwm_mapping_dst_free );
+ avl_free( rwm_oc.map, rwm_mapping_free );
+ avl_free( rwm_at.remap, rwm_mapping_dst_free );
+ avl_free( rwm_at.map, rwm_mapping_free );
+
+ for ( ; cnt-- > c->valx; ) {
+ rwmap->rwm_bva_map[ cnt + 1 ] = rwmap->rwm_bva_map[ cnt ];
+ }
+ rwmap->rwm_bva_map[ c->valx ] = bv;
+
+ } else {
+rwmmap_fail:;
+ avl_free( rwmap->rwm_oc.remap, rwm_mapping_dst_free );
+ avl_free( rwmap->rwm_oc.map, rwm_mapping_free );
+ avl_free( rwmap->rwm_at.remap, rwm_mapping_dst_free );
+ avl_free( rwmap->rwm_at.map, rwm_mapping_free );
+ rwmap->rwm_oc = rwm_oc;
+ rwmap->rwm_at = rwm_at;
+ }
+
+ break;
+ }
+
+ argv0 = c->argv[ 0 ];
+ c->argv[ 0 ] += STRLENOF( "rwm-" );
+ rc = rwm_m_config( &db, c->fname, c->lineno, c->argc, c->argv );
+ c->argv[ 0 ] = argv0;
+ if ( rc ) {
+ return 1;
+
+ } else {
+ char *line;
+ struct berval bv;
+
+ line = ldap_charray2str( &c->argv[ 1 ], " " );
+ if ( line != NULL ) {
+ ber_str2bv( line, 0, 0, &bv );
+ ber_bvarray_add( &rwmap->rwm_bva_map, &bv );
+ }
+ }
+ break;
+
+ case RWM_CF_NORMALIZE_MAPPED:
+ if ( c->value_int ) {
+ rwmap->rwm_flags |= RWM_F_NORMALIZE_MAPPED_ATTRS;
+ } else {
+ rwmap->rwm_flags &= ~RWM_F_NORMALIZE_MAPPED_ATTRS;
+ }
+ break;
+
+ case RWM_CF_DROP_UNREQUESTED:
+ if ( c->value_int ) {
+ rwmap->rwm_flags |= RWM_F_DROP_UNREQUESTED_ATTRS;
+ } else {
+ rwmap->rwm_flags &= ~RWM_F_DROP_UNREQUESTED_ATTRS;
+ }
+ break;
+
+ default:
+ assert( 0 );
+ return 1;
+ }
+
+ return rc;
+}
+
+static int
+rwm_db_init(
+ BackendDB *be,
+ ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+ struct ldaprwmap *rwmap;
+ int rc = 0;
+
+ rwmap = (struct ldaprwmap *)ch_calloc( 1, sizeof( struct ldaprwmap ) );
+
+ /* default */
+ rwmap->rwm_flags = RWM_F_DROP_UNREQUESTED_ATTRS;
+
+ rc = rwm_info_init( &rwmap->rwm_rw );
+
+ on->on_bi.bi_private = (void *)rwmap;
+
+ if ( rc ) {
+ (void)rwm_db_destroy( be, NULL );
+ }
+
+ return rc;
+}
+
+static int
+rwm_db_destroy(
+ BackendDB *be,
+ ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+ int rc = 0;
+
+ if ( on->on_bi.bi_private ) {
+ struct ldaprwmap *rwmap =
+ (struct ldaprwmap *)on->on_bi.bi_private;
+
+ if ( rwmap->rwm_rw ) {
+ rewrite_info_delete( &rwmap->rwm_rw );
+ if ( rwmap->rwm_bva_rewrite )
+ ber_bvarray_free( rwmap->rwm_bva_rewrite );
+ }
+
+ avl_free( rwmap->rwm_oc.remap, rwm_mapping_dst_free );
+ avl_free( rwmap->rwm_oc.map, rwm_mapping_free );
+ avl_free( rwmap->rwm_at.remap, rwm_mapping_dst_free );
+ avl_free( rwmap->rwm_at.map, rwm_mapping_free );
+ ber_bvarray_free( rwmap->rwm_bva_map );
+
+ ch_free( rwmap );
+ }
+
+ return rc;
+}
+
+static slap_overinst rwm = { { NULL } };
+
+#if SLAPD_OVER_RWM == SLAPD_MOD_DYNAMIC
+static
+#endif /* SLAPD_OVER_RWM == SLAPD_MOD_DYNAMIC */
+int
+rwm_initialize( void )
+{
+ int rc;
+
+ /* Make sure we don't exceed the bits reserved for userland */
+ config_check_userland( RWM_CF_LAST );
+
+ memset( &rwm, 0, sizeof( slap_overinst ) );
+
+ rwm.on_bi.bi_type = "rwm";
+ rwm.on_bi.bi_flags =
+ SLAPO_BFLAG_SINGLE |
+ 0;
+
+ rwm.on_bi.bi_db_init = rwm_db_init;
+ rwm.on_bi.bi_db_config = rwm_db_config;
+ rwm.on_bi.bi_db_destroy = rwm_db_destroy;
+
+ rwm.on_bi.bi_op_bind = rwm_op_bind;
+ rwm.on_bi.bi_op_search = rwm_op_search;
+ rwm.on_bi.bi_op_compare = rwm_op_compare;
+ rwm.on_bi.bi_op_modify = rwm_op_modify;
+ rwm.on_bi.bi_op_modrdn = rwm_op_modrdn;
+ rwm.on_bi.bi_op_add = rwm_op_add;
+ rwm.on_bi.bi_op_delete = rwm_op_delete;
+ rwm.on_bi.bi_op_unbind = rwm_op_unbind;
+ rwm.on_bi.bi_extended = rwm_extended;
+#if 1 /* TODO */
+ rwm.on_bi.bi_entry_release_rw = rwm_entry_release_rw;
+ rwm.on_bi.bi_entry_get_rw = rwm_entry_get_rw;
+#endif
+
+ rwm.on_bi.bi_operational = rwm_operational;
+ rwm.on_bi.bi_chk_referrals = 0 /* rwm_chk_referrals */ ;
+
+ rwm.on_bi.bi_connection_init = rwm_conn_init;
+ rwm.on_bi.bi_connection_destroy = rwm_conn_destroy;
+
+ rwm.on_response = rwm_response;
+
+ rwm.on_bi.bi_cf_ocs = rwmocs;
+
+ rc = config_register_schema( rwmcfg, rwmocs );
+ if ( rc ) {
+ return rc;
+ }
+
+ return overlay_register( &rwm );
+}
+
+#if SLAPD_OVER_RWM == SLAPD_MOD_DYNAMIC
+int
+init_module( int argc, char *argv[] )
+{
+ return rwm_initialize();
+}
+#endif /* SLAPD_OVER_RWM == SLAPD_MOD_DYNAMIC */
+
+#endif /* SLAPD_OVER_RWM */
diff --git a/rwm.h b/rwm.h
new file mode 100644
index 0000000..6461894
--- /dev/null
+++ b/rwm.h
@@ -0,0 +1,187 @@
+/* rwm.h - dn rewrite/attribute mapping header file */
+/* $OpenLDAP: pkg/ldap/servers/slapd/overlays/rwm.h,v 1.15.2.8 2010/04/19 19:31:17 quanah Exp $ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2010 The OpenLDAP Foundation.
+ * Portions Copyright 1999-2003 Howard Chu.
+ * Portions Copyright 2000-2003 Pierangelo Masarati.
+ * 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 and subsequently enhanced by Pierangelo
+ * Masarati.
+ */
+
+#ifndef RWM_H
+#define RWM_H
+
+#ifndef ENABLE_REWRITE
+#error "librewrite must be enabled!"
+#endif /* ENABLE_REWRITE */
+
+/* String rewrite library */
+#include "rewrite.h"
+
+LDAP_BEGIN_DECL
+
+/* define to enable referral DN massage by default */
+#undef RWM_REFERRAL_REWRITE
+
+struct ldapmap {
+ int drop_missing;
+
+ Avlnode *map;
+ Avlnode *remap;
+};
+
+struct ldapmapping {
+ int m_flags;
+#define RWMMAP_F_NONE 0x00
+#define RWMMAP_F_IS_OC 0x01
+#define RWMMAP_F_FREE_SRC 0x10
+#define RWMMAP_F_FREE_DST 0x20
+ struct berval m_src;
+ union {
+ AttributeDescription *m_s_ad;
+ ObjectClass *m_s_oc;
+ } m_src_ref;
+#define m_src_ad m_src_ref.m_s_ad
+#define m_src_oc m_src_ref.m_s_oc
+ struct berval m_dst;
+ union {
+ AttributeDescription *m_d_ad;
+ ObjectClass *m_d_oc;
+ } m_dst_ref;
+#define m_dst_ad m_dst_ref.m_d_ad
+#define m_dst_oc m_dst_ref.m_d_oc
+};
+
+struct ldaprwmap {
+ /*
+ * DN rewriting
+ */
+ struct rewrite_info *rwm_rw;
+ BerVarray rwm_bva_rewrite;
+
+ /*
+ * Attribute/objectClass mapping
+ */
+ struct ldapmap rwm_oc;
+ struct ldapmap rwm_at;
+ BerVarray rwm_bva_map;
+
+#define RWM_F_NONE (0x0000U)
+#define RWM_F_NORMALIZE_MAPPED_ATTRS (0x0001U)
+#define RWM_F_DROP_UNREQUESTED_ATTRS (0x0002U)
+#define RWM_F_SUPPORT_T_F (0x4000U)
+#define RWM_F_SUPPORT_T_F_DISCOVER (0x8000U)
+#define RWM_F_SUPPORT_T_F_MASK (RWM_F_SUPPORT_T_F)
+#define RWM_F_SUPPORT_T_F_MASK2 (RWM_F_SUPPORT_T_F|RWM_F_SUPPORT_T_F_DISCOVER)
+ unsigned rwm_flags;
+};
+
+/* Whatever context ldap_back_dn_massage needs... */
+typedef struct dncookie {
+ struct ldaprwmap *rwmap;
+
+ Connection *conn;
+ char *ctx;
+ SlapReply *rs;
+} dncookie;
+
+int rwm_dn_massage( dncookie *dc, struct berval *in, struct berval *dn );
+int rwm_dn_massage_pretty( dncookie *dc, struct berval *in, struct berval *pdn );
+int rwm_dn_massage_normalize( dncookie *dc, struct berval *in, struct berval *ndn );
+int rwm_dn_massage_pretty_normalize( dncookie *dc, struct berval *in, struct berval *pdn, struct berval *ndn );
+
+/* attributeType/objectClass mapping */
+int rwm_mapping_cmp (const void *, const void *);
+int rwm_mapping_dup (void *, void *);
+
+int rwm_map_init ( struct ldapmap *lm, struct ldapmapping ** );
+void rwm_map ( struct ldapmap *map, struct berval *s, struct berval *m,
+ int remap );
+int rwm_mapping ( struct ldapmap *map, struct berval *s,
+ struct ldapmapping **m, int remap );
+#define RWM_MAP 0
+#define RWM_REMAP 1
+char *
+rwm_map_filter(
+ struct ldapmap *at_map,
+ struct ldapmap *oc_map,
+ struct berval *f );
+
+#if 0 /* unused! */
+int
+rwm_map_attrs(
+ struct ldapmap *at_map,
+ AttributeName *a,
+ int remap,
+ char ***mapped_attrs );
+#endif
+
+int
+rwm_map_attrnames(
+ Operation *op,
+ struct ldapmap *at_map,
+ struct ldapmap *oc_map,
+ AttributeName *an,
+ AttributeName **anp,
+ int remap );
+
+extern void rwm_mapping_dst_free ( void *mapping );
+
+extern void rwm_mapping_free ( void *mapping );
+
+extern int rwm_map_config(
+ struct ldapmap *oc_map,
+ struct ldapmap *at_map,
+ const char *fname,
+ int lineno,
+ int argc,
+ char **argv );
+
+extern int
+rwm_filter_map_rewrite(
+ Operation *op,
+ dncookie *dc,
+ Filter *f,
+ struct berval *fstr );
+
+/* suffix massaging by means of librewrite */
+extern int
+rwm_suffix_massage_config(
+ struct rewrite_info *info,
+ struct berval *pvnc,
+ struct berval *nvnc,
+ struct berval *prnc,
+ struct berval *nrnc);
+extern int
+rwm_dnattr_rewrite(
+ Operation *op,
+ SlapReply *rs,
+ void *cookie,
+ BerVarray a_vals,
+ BerVarray *pa_nvals );
+extern int
+rwm_referral_rewrite(
+ Operation *op,
+ SlapReply *rs,
+ void *cookie,
+ BerVarray a_vals,
+ BerVarray *pa_nvals );
+extern int rwm_dnattr_result_rewrite( dncookie *dc, BerVarray a_vals, BerVarray a_nvals );
+extern int rwm_referral_result_rewrite( dncookie *dc, BerVarray a_vals );
+
+LDAP_END_DECL
+
+#endif /* RWM_H */
diff --git a/rwmconf.c b/rwmconf.c
new file mode 100644
index 0000000..7a89f2d
--- /dev/null
+++ b/rwmconf.c
@@ -0,0 +1,417 @@
+/* rwmconf.c - rewrite/map configuration file routines */
+/* $OpenLDAP: pkg/ldap/servers/slapd/overlays/rwmconf.c,v 1.25.2.7 2010/06/10 17:37:40 quanah Exp $ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2010 The OpenLDAP Foundation.
+ * Portions Copyright 1999-2003 Howard Chu.
+ * Portions Copyright 2000-2003 Pierangelo Masarati.
+ * 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 and subsequently enhanced by Pierangelo
+ * Masarati.
+ */
+
+#include "portable.h"
+
+#ifdef SLAPD_OVER_RWM
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "rwm.h"
+#include "lutil.h"
+
+int
+rwm_map_config(
+ struct ldapmap *oc_map,
+ struct ldapmap *at_map,
+ const char *fname,
+ int lineno,
+ int argc,
+ char **argv )
+{
+ struct ldapmap *map;
+ struct ldapmapping *mapping;
+ char *src, *dst;
+ int is_oc = 0;
+ int rc = 0;
+
+ if ( argc < 3 || argc > 4 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: syntax is \"map {objectclass | attribute} [<local> | *] {<foreign> | *}\"\n",
+ fname, lineno, 0 );
+ return 1;
+ }
+
+ if ( strcasecmp( argv[1], "objectclass" ) == 0 ) {
+ map = oc_map;
+ is_oc = 1;
+
+ } else if ( strcasecmp( argv[1], "attribute" ) == 0 ) {
+ map = at_map;
+
+ } else {
+ Debug( LDAP_DEBUG_ANY, "%s: line %d: syntax is "
+ "\"map {objectclass | attribute} [<local> | *] "
+ "{<foreign> | *}\"\n",
+ fname, lineno, 0 );
+ return 1;
+ }
+
+ if ( !is_oc && map->map == NULL ) {
+ /* only init if required */
+ if ( rwm_map_init( map, &mapping ) != LDAP_SUCCESS ) {
+ return 1;
+ }
+ }
+
+ if ( strcmp( argv[2], "*" ) == 0 ) {
+ if ( argc < 4 || strcmp( argv[3], "*" ) == 0 ) {
+ map->drop_missing = ( argc < 4 );
+ goto success_return;
+ }
+ src = dst = argv[3];
+
+ } else if ( argc < 4 ) {
+ src = "";
+ dst = argv[2];
+
+ } else {
+ src = argv[2];
+ dst = ( strcmp( argv[3], "*" ) == 0 ? src : argv[3] );
+ }
+
+ if ( ( map == at_map )
+ && ( strcasecmp( src, "objectclass" ) == 0
+ || strcasecmp( dst, "objectclass" ) == 0 ) )
+ {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: objectclass attribute cannot be mapped\n",
+ fname, lineno, 0 );
+ return 1;
+ }
+
+ mapping = (struct ldapmapping *)ch_calloc( 2,
+ sizeof(struct ldapmapping) );
+ if ( mapping == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: out of memory\n",
+ fname, lineno, 0 );
+ return 1;
+ }
+ ber_str2bv( src, 0, 1, &mapping[0].m_src );
+ ber_str2bv( dst, 0, 1, &mapping[0].m_dst );
+ mapping[1].m_src = mapping[0].m_dst;
+ mapping[1].m_dst = mapping[0].m_src;
+
+ mapping[0].m_flags = RWMMAP_F_NONE;
+ mapping[1].m_flags = RWMMAP_F_NONE;
+
+ /*
+ * schema check
+ */
+ if ( is_oc ) {
+ if ( src[0] != '\0' ) {
+ mapping[0].m_src_oc = oc_bvfind( &mapping[0].m_src );
+ if ( mapping[0].m_src_oc == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: warning, source objectClass '%s' "
+ "should be defined in schema\n",
+ fname, lineno, src );
+
+ /*
+ * FIXME: this should become an err
+ */
+ mapping[0].m_src_oc = ch_malloc( sizeof( ObjectClass ) );
+ memset( mapping[0].m_src_oc, 0, sizeof( ObjectClass ) );
+ mapping[0].m_src_oc->soc_cname = mapping[0].m_src;
+ mapping[0].m_flags |= RWMMAP_F_FREE_SRC;
+ }
+ mapping[1].m_dst_oc = mapping[0].m_src_oc;
+ }
+
+ mapping[0].m_dst_oc = oc_bvfind( &mapping[0].m_dst );
+ if ( mapping[0].m_dst_oc == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: warning, destination objectClass '%s' "
+ "is not defined in schema\n",
+ fname, lineno, dst );
+
+ mapping[0].m_dst_oc = oc_bvfind_undef( &mapping[0].m_dst );
+ if ( mapping[0].m_dst_oc == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "%s: line %d: unable to mimic destination objectClass '%s'\n",
+ fname, lineno, dst );
+ goto error_return;
+ }
+ }
+ mapping[1].m_src_oc = mapping[0].m_dst_oc;
+
+ mapping[0].m_flags |= RWMMAP_F_IS_OC;
+ mapping[1].m_flags |= RWMMAP_F_IS_OC;
+
+ } else {
+ int rc;
+ const char *text = NULL;
+
+ if ( src[0] != '\0' ) {
+ rc = slap_bv2ad( &mapping[0].m_src,
+ &mapping[0].m_src_ad, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: warning, source attributeType '%s' "
+ "should be defined in schema\n",
+ fname, lineno, src );
+
+ /*
+ * we create a fake "proxied" ad
+ * and add it here.
+ */
+
+ rc = slap_bv2undef_ad( &mapping[0].m_src,
+ &mapping[0].m_src_ad, &text,
+ SLAP_AD_PROXIED );
+ if ( rc != LDAP_SUCCESS ) {
+ char prefix[1024];
+ snprintf( prefix, sizeof(prefix),
+ "%s: line %d: source attributeType '%s': %d",
+ fname, lineno, src, rc );
+ Debug( LDAP_DEBUG_ANY, "%s (%s)\n",
+ prefix, text ? text : "null", 0 );
+ goto error_return;
+ }
+
+ }
+ mapping[1].m_dst_ad = mapping[0].m_src_ad;
+ }
+
+ rc = slap_bv2ad( &mapping[0].m_dst, &mapping[0].m_dst_ad, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: warning, destination attributeType '%s' "
+ "is not defined in schema\n",
+ fname, lineno, dst );
+
+ rc = slap_bv2undef_ad( &mapping[0].m_dst,
+ &mapping[0].m_dst_ad, &text,
+ SLAP_AD_PROXIED );
+ if ( rc != LDAP_SUCCESS ) {
+ char prefix[1024];
+ snprintf( prefix, sizeof(prefix),
+ "%s: line %d: destination attributeType '%s': %d",
+ fname, lineno, dst, rc );
+ Debug( LDAP_DEBUG_ANY, "%s (%s)\n",
+ prefix, text ? text : "null", 0 );
+ goto error_return;
+ }
+ }
+ mapping[1].m_src_ad = mapping[0].m_dst_ad;
+ }
+
+ if ( ( src[0] != '\0' && avl_find( map->map, (caddr_t)mapping, rwm_mapping_cmp ) != NULL)
+ || avl_find( map->remap, (caddr_t)&mapping[1], rwm_mapping_cmp ) != NULL)
+ {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: duplicate mapping found.\n",
+ fname, lineno, 0 );
+ /* FIXME: free stuff */
+ goto error_return;
+ }
+
+ if ( src[0] != '\0' ) {
+ avl_insert( &map->map, (caddr_t)&mapping[0],
+ rwm_mapping_cmp, rwm_mapping_dup );
+ }
+ avl_insert( &map->remap, (caddr_t)&mapping[1],
+ rwm_mapping_cmp, rwm_mapping_dup );
+
+success_return:;
+ return rc;
+
+error_return:;
+ if ( mapping ) {
+ rwm_mapping_free( mapping );
+ }
+
+ return 1;
+}
+
+static char *
+rwm_suffix_massage_regexize( const char *s )
+{
+ char *res, *ptr;
+ const char *p, *r;
+ int i;
+
+ if ( s[0] == '\0' ) {
+ return ch_strdup( "^(.+)$" );
+ }
+
+ for ( i = 0, p = s;
+ ( r = strchr( p, ',' ) ) != NULL;
+ p = r + 1, i++ )
+ ;
+
+ res = ch_calloc( sizeof( char ), strlen( s )
+ + STRLENOF( "((.+),)?" )
+ + STRLENOF( "[ ]?" ) * i
+ + STRLENOF( "$" ) + 1 );
+
+ ptr = lutil_strcopy( res, "((.+),)?" );
+ for ( i = 0, p = s;
+ ( r = strchr( p, ',' ) ) != NULL;
+ p = r + 1 , i++ ) {
+ ptr = lutil_strncopy( ptr, p, r - p + 1 );
+ ptr = lutil_strcopy( ptr, "[ ]?" );
+
+ if ( r[ 1 ] == ' ' ) {
+ r++;
+ }
+ }
+ ptr = lutil_strcopy( ptr, p );
+ ptr[0] = '$';
+ ptr[1] = '\0';
+
+ return res;
+}
+
+static char *
+rwm_suffix_massage_patternize( const char *s, const char *p )
+{
+ ber_len_t len;
+ char *res, *ptr;
+
+ len = strlen( p );
+
+ if ( s[ 0 ] == '\0' ) {
+ len++;
+ }
+
+ res = ch_calloc( sizeof( char ), len + STRLENOF( "%1" ) + 1 );
+ if ( res == NULL ) {
+ return NULL;
+ }
+
+ ptr = lutil_strcopy( res, ( p[0] == '\0' ? "%2" : "%1" ) );
+ if ( s[ 0 ] == '\0' ) {
+ ptr[ 0 ] = ',';
+ ptr++;
+ }
+ lutil_strcopy( ptr, p );
+
+ return res;
+}
+
+int
+rwm_suffix_massage_config(
+ struct rewrite_info *info,
+ struct berval *pvnc,
+ struct berval *nvnc,
+ struct berval *prnc,
+ struct berval *nrnc
+)
+{
+ char *rargv[ 5 ];
+ int line = 0;
+
+ rargv[ 0 ] = "rewriteEngine";
+ rargv[ 1 ] = "on";
+ rargv[ 2 ] = NULL;
+ rewrite_parse( info, "<suffix massage>", ++line, 2, rargv );
+
+ rargv[ 0 ] = "rewriteContext";
+ rargv[ 1 ] = "default";
+ rargv[ 2 ] = NULL;
+ rewrite_parse( info, "<suffix massage>", ++line, 2, rargv );
+
+ rargv[ 0 ] = "rewriteRule";
+ rargv[ 1 ] = rwm_suffix_massage_regexize( pvnc->bv_val );
+ rargv[ 2 ] = rwm_suffix_massage_patternize( pvnc->bv_val, prnc->bv_val );
+ rargv[ 3 ] = ":";
+ rargv[ 4 ] = NULL;
+ rewrite_parse( info, "<suffix massage>", ++line, 4, rargv );
+ ch_free( rargv[ 1 ] );
+ ch_free( rargv[ 2 ] );
+
+ if ( BER_BVISEMPTY( pvnc ) ) {
+ rargv[ 0 ] = "rewriteRule";
+ rargv[ 1 ] = "^$";
+ rargv[ 2 ] = prnc->bv_val;
+ rargv[ 3 ] = ":";
+ rargv[ 4 ] = NULL;
+ rewrite_parse( info, "<suffix massage>", ++line, 4, rargv );
+ }
+
+ rargv[ 0 ] = "rewriteContext";
+ rargv[ 1 ] = "searchEntryDN";
+ rargv[ 2 ] = NULL;
+ rewrite_parse( info, "<suffix massage>", ++line, 2, rargv );
+
+ rargv[ 0 ] = "rewriteRule";
+ rargv[ 1 ] = rwm_suffix_massage_regexize( prnc->bv_val );
+ rargv[ 2 ] = rwm_suffix_massage_patternize( prnc->bv_val, pvnc->bv_val );
+ rargv[ 3 ] = ":";
+ rargv[ 4 ] = NULL;
+ rewrite_parse( info, "<suffix massage>", ++line, 4, rargv );
+ ch_free( rargv[ 1 ] );
+ ch_free( rargv[ 2 ] );
+
+ if ( BER_BVISEMPTY( prnc ) ) {
+ rargv[ 0 ] = "rewriteRule";
+ rargv[ 1 ] = "^$";
+ rargv[ 2 ] = pvnc->bv_val;
+ rargv[ 3 ] = ":";
+ rargv[ 4 ] = NULL;
+ rewrite_parse( info, "<suffix massage>", ++line, 4, rargv );
+ }
+
+ rargv[ 0 ] = "rewriteContext";
+ rargv[ 1 ] = "matchedDN";
+ rargv[ 2 ] = "alias";
+ rargv[ 3 ] = "searchEntryDN";
+ rargv[ 4 ] = NULL;
+ rewrite_parse( info, "<suffix massage>", ++line, 4, rargv );
+
+#ifdef RWM_REFERRAL_REWRITE
+ /* FIXME: we don't want this on by default, do we? */
+ rargv[ 0 ] = "rewriteContext";
+ rargv[ 1 ] = "referralDN";
+ rargv[ 2 ] = "alias";
+ rargv[ 3 ] = "searchEntryDN";
+ rargv[ 4 ] = NULL;
+ rewrite_parse( info, "<suffix massage>", ++line, 4, rargv );
+#else /* ! RWM_REFERRAL_REWRITE */
+ rargv[ 0 ] = "rewriteContext";
+ rargv[ 1 ] = "referralAttrDN";
+ rargv[ 2 ] = NULL;
+ rewrite_parse( info, "<suffix massage>", ++line, 2, rargv );
+
+ rargv[ 0 ] = "rewriteContext";
+ rargv[ 1 ] = "referralDN";
+ rargv[ 2 ] = NULL;
+ rewrite_parse( info, "<suffix massage>", ++line, 2, rargv );
+#endif /* ! RWM_REFERRAL_REWRITE */
+
+ rargv[ 0 ] = "rewriteContext";
+ rargv[ 1 ] = "searchAttrDN";
+ rargv[ 2 ] = "alias";
+ rargv[ 3 ] = "searchEntryDN";
+ rargv[ 4 ] = NULL;
+ rewrite_parse( info, "<suffix massage>", ++line, 4, rargv );
+
+ return 0;
+}
+
+#endif /* SLAPD_OVER_RWM */
diff --git a/rwmdn.c b/rwmdn.c
new file mode 100644
index 0000000..6f12b53
--- /dev/null
+++ b/rwmdn.c
@@ -0,0 +1,215 @@
+/* rwmdn.c - massages dns */
+/* $OpenLDAP: pkg/ldap/servers/slapd/overlays/rwmdn.c,v 1.18.2.6 2010/04/13 20:23:46 kurt Exp $ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2010 The OpenLDAP Foundation.
+ * Portions Copyright 1999-2003 Howard Chu.
+ * Portions Copyright 2000-2003 Pierangelo Masarati.
+ * 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 Howard Chu for inclusion
+ * in OpenLDAP Software and subsequently enhanced by Pierangelo
+ * Masarati.
+ */
+
+
+#include "portable.h"
+
+#ifdef SLAPD_OVER_RWM
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "rwm.h"
+
+/* FIXME: after rewriting, we should also remap attributes ... */
+
+/*
+ * massages "in" and normalizes it into "ndn"
+ *
+ * "ndn" may be untouched if no massaging occurred and its value was not null
+ */
+int
+rwm_dn_massage_normalize(
+ dncookie *dc,
+ struct berval *in,
+ struct berval *ndn )
+{
+ int rc;
+ struct berval mdn = BER_BVNULL;
+
+ /* massage and normalize a DN */
+ rc = rwm_dn_massage( dc, in, &mdn );
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+
+ if ( mdn.bv_val == in->bv_val && !BER_BVISNULL( ndn ) ) {
+ return rc;
+ }
+
+ rc = dnNormalize( 0, NULL, NULL, &mdn, ndn, NULL );
+
+ if ( mdn.bv_val != in->bv_val ) {
+ ch_free( mdn.bv_val );
+ }
+
+ return rc;
+}
+
+/*
+ * massages "in" and prettifies it into "pdn"
+ *
+ * "pdn" may be untouched if no massaging occurred and its value was not null
+ */
+int
+rwm_dn_massage_pretty(
+ dncookie *dc,
+ struct berval *in,
+ struct berval *pdn )
+{
+ int rc;
+ struct berval mdn = BER_BVNULL;
+
+ /* massage and pretty a DN */
+ rc = rwm_dn_massage( dc, in, &mdn );
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+
+ if ( mdn.bv_val == in->bv_val && !BER_BVISNULL( pdn ) ) {
+ return rc;
+ }
+
+ rc = dnPretty( NULL, &mdn, pdn, NULL );
+
+ if ( mdn.bv_val != in->bv_val ) {
+ ch_free( mdn.bv_val );
+ }
+
+ return rc;
+}
+
+/*
+ * massages "in" and prettifies and normalizes it into "pdn" and "ndn"
+ *
+ * "pdn" may be untouched if no massaging occurred and its value was not null;
+ * "ndn" may be untouched if no massaging occurred and its value was not null;
+ * if no massage occurred and "ndn" value was not null, it is filled
+ * with the normaized value of "pdn", much like ndn = dnNormalize( pdn )
+ */
+int
+rwm_dn_massage_pretty_normalize(
+ dncookie *dc,
+ struct berval *in,
+ struct berval *pdn,
+ struct berval *ndn )
+{
+ int rc;
+ struct berval mdn = BER_BVNULL;
+
+ /* massage, pretty and normalize a DN */
+ rc = rwm_dn_massage( dc, in, &mdn );
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+
+ if ( mdn.bv_val == in->bv_val && !BER_BVISNULL( pdn ) ) {
+ if ( BER_BVISNULL( ndn ) ) {
+ rc = dnNormalize( 0, NULL, NULL, &mdn, ndn, NULL );
+ }
+ return rc;
+ }
+
+ rc = dnPrettyNormal( NULL, &mdn, pdn, ndn, NULL );
+
+ if ( mdn.bv_val != in->bv_val ) {
+ ch_free( mdn.bv_val );
+ }
+
+ return rc;
+}
+
+/*
+ * massages "in" into "dn"
+ *
+ * "dn" may contain the value of "in" if no massage occurred
+ */
+int
+rwm_dn_massage(
+ dncookie *dc,
+ struct berval *in,
+ struct berval *dn
+)
+{
+ int rc = 0;
+ struct berval mdn;
+ static char *dmy = "";
+ char *in_val;
+
+ assert( dc != NULL );
+ assert( in != NULL );
+ assert( dn != NULL );
+
+ /* protect from NULL berval */
+ in_val = in->bv_val ? in->bv_val : dmy;
+
+ rc = rewrite_session( dc->rwmap->rwm_rw, dc->ctx,
+ in_val, dc->conn, &mdn.bv_val );
+ switch ( rc ) {
+ case REWRITE_REGEXEC_OK:
+ if ( !BER_BVISNULL( &mdn ) && mdn.bv_val != in_val ) {
+ mdn.bv_len = strlen( mdn.bv_val );
+ *dn = mdn;
+ } else {
+ dn->bv_len = in->bv_len;
+ dn->bv_val = in_val;
+ }
+ rc = LDAP_SUCCESS;
+
+ Debug( LDAP_DEBUG_ARGS,
+ "[rw] %s: \"%s\" -> \"%s\"\n",
+ dc->ctx, in_val, dn->bv_val );
+ break;
+
+ case REWRITE_REGEXEC_UNWILLING:
+ if ( dc->rs ) {
+ dc->rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ dc->rs->sr_text = "Operation not allowed";
+ }
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ break;
+
+ case REWRITE_REGEXEC_ERR:
+ if ( dc->rs ) {
+ dc->rs->sr_err = LDAP_OTHER;
+ dc->rs->sr_text = "Rewrite error";
+ }
+ rc = LDAP_OTHER;
+ break;
+ }
+
+ if ( mdn.bv_val == dmy ) {
+ BER_BVZERO( &mdn );
+ }
+
+ if ( dn->bv_val == dmy ) {
+ BER_BVZERO( dn );
+ }
+
+ return rc;
+}
+
+#endif /* SLAPD_OVER_RWM */
diff --git a/rwmmap.c b/rwmmap.c
new file mode 100644
index 0000000..2982445
--- /dev/null
+++ b/rwmmap.c
@@ -0,0 +1,1313 @@
+/* rwmmap.c - rewrite/mapping routines */
+/* $OpenLDAP: pkg/ldap/servers/slapd/overlays/rwmmap.c,v 1.31.2.15 2010/04/19 19:31:17 quanah Exp $ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2010 The OpenLDAP Foundation.
+ * Portions Copyright 1999-2003 Howard Chu.
+ * Portions Copyright 2000-2003 Pierangelo Masarati.
+ * 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 and subsequently enhanced by Pierangelo
+ * Masarati.
+ */
+
+#include "portable.h"
+
+#ifdef SLAPD_OVER_RWM
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "rwm.h"
+
+#undef ldap_debug /* silence a warning in ldap-int.h */
+#include "../../../libraries/libldap/ldap-int.h"
+
+int
+rwm_mapping_cmp( const void *c1, const void *c2 )
+{
+ struct ldapmapping *map1 = (struct ldapmapping *)c1;
+ struct ldapmapping *map2 = (struct ldapmapping *)c2;
+ int rc = map1->m_src.bv_len - map2->m_src.bv_len;
+
+ if ( rc ) {
+ return rc;
+ }
+
+ return strcasecmp( map1->m_src.bv_val, map2->m_src.bv_val );
+}
+
+int
+rwm_mapping_dup( void *c1, void *c2 )
+{
+ struct ldapmapping *map1 = (struct ldapmapping *)c1;
+ struct ldapmapping *map2 = (struct ldapmapping *)c2;
+ int rc = map1->m_src.bv_len - map2->m_src.bv_len;
+
+ if ( rc ) {
+ return 0;
+ }
+
+ return ( ( strcasecmp( map1->m_src.bv_val, map2->m_src.bv_val ) == 0 ) ? -1 : 0 );
+}
+
+int
+rwm_map_init( struct ldapmap *lm, struct ldapmapping **m )
+{
+ struct ldapmapping *mapping;
+ const char *text;
+ int rc;
+
+ assert( m != NULL );
+
+ *m = NULL;
+
+ mapping = (struct ldapmapping *)ch_calloc( 2,
+ sizeof( struct ldapmapping ) );
+ if ( mapping == NULL ) {
+ return LDAP_NO_MEMORY;
+ }
+
+ /* NOTE: this is needed to make sure that
+ * rwm-map attribute *
+ * does not filter out all attributes including objectClass */
+ rc = slap_str2ad( "objectClass", &mapping[0].m_src_ad, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ ch_free( mapping );
+ return rc;
+ }
+
+ mapping[0].m_dst_ad = mapping[0].m_src_ad;
+ ber_dupbv( &mapping[0].m_src, &mapping[0].m_src_ad->ad_cname );
+ ber_dupbv( &mapping[0].m_dst, &mapping[0].m_src );
+
+ mapping[1].m_src = mapping[0].m_src;
+ mapping[1].m_dst = mapping[0].m_dst;
+ mapping[1].m_src_ad = mapping[0].m_src_ad;
+ mapping[1].m_dst_ad = mapping[1].m_src_ad;
+
+ avl_insert( &lm->map, (caddr_t)&mapping[0],
+ rwm_mapping_cmp, rwm_mapping_dup );
+ avl_insert( &lm->remap, (caddr_t)&mapping[1],
+ rwm_mapping_cmp, rwm_mapping_dup );
+
+ *m = mapping;
+
+ return rc;
+}
+
+int
+rwm_mapping( struct ldapmap *map, struct berval *s, struct ldapmapping **m, int remap )
+{
+ Avlnode *tree;
+ struct ldapmapping fmapping;
+
+ if ( map == NULL ) {
+ return 0;
+ }
+
+ assert( m != NULL );
+
+ /* let special attrnames slip through (ITS#5760) */
+ if ( bvmatch( s, slap_bv_no_attrs )
+ || bvmatch( s, slap_bv_all_user_attrs )
+ || bvmatch( s, slap_bv_all_operational_attrs ) )
+ {
+ *m = NULL;
+ return 0;
+ }
+
+ if ( remap == RWM_REMAP ) {
+ tree = map->remap;
+
+ } else {
+ tree = map->map;
+ }
+
+ fmapping.m_src = *s;
+ *m = (struct ldapmapping *)avl_find( tree, (caddr_t)&fmapping,
+ rwm_mapping_cmp );
+
+ if ( *m == NULL ) {
+ return map->drop_missing;
+ }
+
+ return 0;
+}
+
+void
+rwm_map( struct ldapmap *map, struct berval *s, struct berval *bv, int remap )
+{
+ struct ldapmapping *mapping;
+
+ /* map->map may be NULL when mapping is configured,
+ * but map->remap can't */
+ if ( map->remap == NULL ) {
+ *bv = *s;
+ return;
+ }
+
+ BER_BVZERO( bv );
+ ( void )rwm_mapping( map, s, &mapping, remap );
+ if ( mapping != NULL ) {
+ if ( !BER_BVISNULL( &mapping->m_dst ) ) {
+ *bv = mapping->m_dst;
+ }
+ return;
+ }
+
+ if ( !map->drop_missing ) {
+ *bv = *s;
+ }
+}
+
+/*
+ * Map attribute names in place
+ */
+int
+rwm_map_attrnames(
+ Operation *op,
+ struct ldapmap *at_map,
+ struct ldapmap *oc_map,
+ AttributeName *an,
+ AttributeName **anp,
+ int remap )
+{
+ int i, j;
+
+ assert( anp != NULL );
+
+ *anp = NULL;
+
+ if ( an == NULL ) {
+ return LDAP_SUCCESS;
+ }
+
+ for ( i = 0; !BER_BVISNULL( &an[i].an_name ); i++ )
+ /* just count */ ;
+ *anp = op->o_tmpalloc( ( i + 1 )* sizeof( AttributeName ),
+ op->o_tmpmemctx );
+ if ( *anp == NULL ) {
+ return LDAP_NO_MEMORY;
+ }
+
+ for ( i = 0, j = 0; !BER_BVISNULL( &an[i].an_name ); i++ ) {
+ struct ldapmapping *m;
+ int at_drop_missing = 0,
+ oc_drop_missing = 0;
+
+ if ( an[i].an_desc ) {
+ if ( !at_map ) {
+ /* FIXME: better leave as is? */
+ continue;
+ }
+
+ at_drop_missing = rwm_mapping( at_map, &an[i].an_name, &m, remap );
+ if ( at_drop_missing || ( m && BER_BVISNULL( &m->m_dst ) ) ) {
+ continue;
+ }
+
+ if ( !m ) {
+ (*anp)[j] = an[i];
+ j++;
+ continue;
+ }
+
+ (*anp)[j] = an[i];
+ if ( remap == RWM_MAP ) {
+ (*anp)[j].an_name = m->m_dst;
+ (*anp)[j].an_desc = m->m_dst_ad;
+ } else {
+ (*anp)[j].an_name = m->m_src;
+ (*anp)[j].an_desc = m->m_src_ad;
+
+ }
+
+ j++;
+ continue;
+
+ } else if ( an[i].an_oc ) {
+ if ( !oc_map ) {
+ /* FIXME: better leave as is? */
+ continue;
+ }
+
+ oc_drop_missing = rwm_mapping( oc_map, &an[i].an_name, &m, remap );
+
+ if ( oc_drop_missing || ( m && BER_BVISNULL( &m->m_dst ) ) ) {
+ continue;
+ }
+
+ if ( !m ) {
+ (*anp)[j] = an[i];
+ j++;
+ continue;
+ }
+
+ (*anp)[j] = an[i];
+ if ( remap == RWM_MAP ) {
+ (*anp)[j].an_name = m->m_dst;
+ (*anp)[j].an_oc = m->m_dst_oc;
+ } else {
+ (*anp)[j].an_name = m->m_src;
+ (*anp)[j].an_oc = m->m_src_oc;
+ }
+
+ } else {
+ at_drop_missing = rwm_mapping( at_map, &an[i].an_name, &m, remap );
+
+ if ( at_drop_missing || !m ) {
+ oc_drop_missing = rwm_mapping( oc_map, &an[i].an_name, &m, remap );
+
+ /* if both at_map and oc_map required to drop missing,
+ * then do it */
+ if ( oc_drop_missing && at_drop_missing ) {
+ continue;
+ }
+
+ /* if no oc_map mapping was found and at_map required
+ * to drop missing, then do it; otherwise, at_map wins
+ * and an is considered an attr and is left unchanged */
+ if ( !m ) {
+ if ( at_drop_missing ) {
+ continue;
+ }
+ (*anp)[j] = an[i];
+ j++;
+ continue;
+ }
+
+ if ( BER_BVISNULL( &m->m_dst ) ) {
+ continue;
+ }
+
+ (*anp)[j] = an[i];
+ if ( remap == RWM_MAP ) {
+ (*anp)[j].an_name = m->m_dst;
+ (*anp)[j].an_oc = m->m_dst_oc;
+ } else {
+ (*anp)[j].an_name = m->m_src;
+ (*anp)[j].an_oc = m->m_src_oc;
+ }
+ j++;
+ continue;
+ }
+
+ if ( !BER_BVISNULL( &m->m_dst ) ) {
+ (*anp)[j] = an[i];
+ if ( remap == RWM_MAP ) {
+ (*anp)[j].an_name = m->m_dst;
+ (*anp)[j].an_desc = m->m_dst_ad;
+ } else {
+ (*anp)[j].an_name = m->m_src;
+ (*anp)[j].an_desc = m->m_src_ad;
+ }
+ j++;
+ continue;
+ }
+ }
+ }
+
+ if ( j == 0 && i != 0 ) {
+ memset( &(*anp)[0], 0, sizeof( AttributeName ) );
+ (*anp)[0].an_name = *slap_bv_no_attrs;
+ j = 1;
+ }
+ memset( &(*anp)[j], 0, sizeof( AttributeName ) );
+
+ return LDAP_SUCCESS;
+}
+
+#if 0 /* unused! */
+int
+rwm_map_attrs(
+ struct ldapmap *at_map,
+ AttributeName *an,
+ int remap,
+ char ***mapped_attrs )
+{
+ int i, j;
+ char **na;
+
+ if ( an == NULL ) {
+ *mapped_attrs = NULL;
+ return LDAP_SUCCESS;
+ }
+
+ for ( i = 0; !BER_BVISNULL( &an[ i ].an_name ); i++ )
+ /* count'em */ ;
+
+ na = (char **)ch_calloc( i + 1, sizeof( char * ) );
+ if ( na == NULL ) {
+ *mapped_attrs = NULL;
+ return LDAP_NO_MEMORY;
+ }
+
+ for ( i = j = 0; !BER_BVISNULL( &an[i].an_name ); i++ ) {
+ struct ldapmapping *mapping;
+
+ if ( rwm_mapping( at_map, &an[i].an_name, &mapping, remap ) ) {
+ continue;
+ }
+
+ if ( !mapping ) {
+ na[ j++ ] = an[ i ].an_name.bv_val;
+
+ } else if ( !BER_BVISNULL( &mapping->m_dst ) ) {
+ na[ j++ ] = mapping->m_dst.bv_val;
+ }
+ }
+
+ if ( j == 0 && i != 0 ) {
+ na[ j++ ] = LDAP_NO_ATTRS;
+ }
+
+ na[ j ] = NULL;
+
+ *mapped_attrs = na;
+
+ return LDAP_SUCCESS;
+}
+#endif
+
+static int
+map_attr_value(
+ dncookie *dc,
+ AttributeDescription **adp,
+ struct berval *mapped_attr,
+ struct berval *value,
+ struct berval *mapped_value,
+ int remap,
+ void *memctx )
+{
+ struct berval vtmp = BER_BVNULL;
+ int freeval = 0;
+ AttributeDescription *ad = *adp;
+ struct ldapmapping *mapping = NULL;
+
+ rwm_mapping( &dc->rwmap->rwm_at, &ad->ad_cname, &mapping, remap );
+ if ( mapping == NULL ) {
+ if ( dc->rwmap->rwm_at.drop_missing ) {
+ return -1;
+ }
+
+ *mapped_attr = ad->ad_cname;
+
+ } else {
+ *mapped_attr = mapping->m_dst;
+ }
+
+ if ( value != NULL ) {
+ assert( mapped_value != NULL );
+
+ if ( ad->ad_type->sat_syntax == slap_schema.si_syn_distinguishedName
+ || ( mapping != NULL && mapping->m_dst_ad->ad_type->sat_syntax == slap_schema.si_syn_distinguishedName ) )
+ {
+ dncookie fdc = *dc;
+ int rc;
+
+ fdc.ctx = "searchFilterAttrDN";
+
+ vtmp = *value;
+ rc = rwm_dn_massage_normalize( &fdc, value, &vtmp );
+ switch ( rc ) {
+ case LDAP_SUCCESS:
+ if ( vtmp.bv_val != value->bv_val ) {
+ freeval = 1;
+ }
+ break;
+
+ case LDAP_UNWILLING_TO_PERFORM:
+ case LDAP_OTHER:
+ default:
+ return -1;
+ }
+
+ } else if ( ad->ad_type->sat_equality->smr_usage & SLAP_MR_MUTATION_NORMALIZER ) {
+ if ( ad->ad_type->sat_equality->smr_normalize(
+ (SLAP_MR_DENORMALIZE|SLAP_MR_VALUE_OF_ASSERTION_SYNTAX),
+ NULL, NULL, value, &vtmp, memctx ) )
+ {
+ return -1;
+ }
+ freeval = 2;
+
+ } else if ( ad == slap_schema.si_ad_objectClass
+ || ad == slap_schema.si_ad_structuralObjectClass )
+ {
+ rwm_map( &dc->rwmap->rwm_oc, value, &vtmp, remap );
+ if ( BER_BVISNULL( &vtmp ) || BER_BVISEMPTY( &vtmp ) ) {
+ vtmp = *value;
+ }
+
+ } else {
+ vtmp = *value;
+ }
+
+ filter_escape_value_x( &vtmp, mapped_value, memctx );
+
+ switch ( freeval ) {
+ case 1:
+ ch_free( vtmp.bv_val );
+ break;
+
+ case 2:
+ ber_memfree_x( vtmp.bv_val, memctx );
+ break;
+ }
+ }
+
+ if ( mapping != NULL ) {
+ assert( mapping->m_dst_ad != NULL );
+ *adp = mapping->m_dst_ad;
+ }
+
+ return 0;
+}
+
+static int
+rwm_int_filter_map_rewrite(
+ Operation *op,
+ dncookie *dc,
+ Filter *f,
+ struct berval *fstr )
+{
+ int i;
+ Filter *p;
+ AttributeDescription *ad;
+ struct berval atmp,
+ vtmp,
+ *tmp;
+ static struct berval
+ /* better than nothing... */
+ ber_bvfalse = BER_BVC( "(!(objectClass=*))" ),
+ ber_bvtf_false = BER_BVC( "(|)" ),
+ /* better than nothing... */
+ ber_bvtrue = BER_BVC( "(objectClass=*)" ),
+ ber_bvtf_true = BER_BVC( "(&)" ),
+#if 0
+ /* no longer needed; preserved for completeness */
+ ber_bvundefined = BER_BVC( "(?=undefined)" ),
+#endif
+ ber_bverror = BER_BVC( "(?=error)" ),
+ ber_bvunknown = BER_BVC( "(?=unknown)" ),
+ ber_bvnone = BER_BVC( "(?=none)" );
+ ber_len_t len;
+
+ assert( fstr != NULL );
+ BER_BVZERO( fstr );
+
+ if ( f == NULL ) {
+ ber_dupbv_x( fstr, &ber_bvnone, op->o_tmpmemctx );
+ return LDAP_OTHER;
+ }
+
+ if ( f->f_choice & SLAPD_FILTER_UNDEFINED ) {
+ goto computed;
+ }
+
+ switch ( f->f_choice & SLAPD_FILTER_MASK ) {
+ case LDAP_FILTER_EQUALITY:
+ ad = f->f_av_desc;
+ if ( map_attr_value( dc, &ad, &atmp,
+ &f->f_av_value, &vtmp, RWM_MAP, op->o_tmpmemctx ) )
+ {
+ goto computed;
+ }
+
+ fstr->bv_len = atmp.bv_len + vtmp.bv_len + STRLENOF( "(=)" );
+ fstr->bv_val = op->o_tmpalloc( fstr->bv_len + 1, op->o_tmpmemctx );
+
+ snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s=%s)",
+ atmp.bv_val, vtmp.bv_len ? vtmp.bv_val : "" );
+
+ op->o_tmpfree( vtmp.bv_val, op->o_tmpmemctx );
+ break;
+
+ case LDAP_FILTER_GE:
+ ad = f->f_av_desc;
+ if ( map_attr_value( dc, &ad, &atmp,
+ &f->f_av_value, &vtmp, RWM_MAP, op->o_tmpmemctx ) )
+ {
+ goto computed;
+ }
+
+ fstr->bv_len = atmp.bv_len + vtmp.bv_len + STRLENOF( "(>=)" );
+ fstr->bv_val = op->o_tmpalloc( fstr->bv_len + 1, op->o_tmpmemctx );
+
+ snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s>=%s)",
+ atmp.bv_val, vtmp.bv_len ? vtmp.bv_val : "" );
+
+ op->o_tmpfree( vtmp.bv_val, op->o_tmpmemctx );
+ break;
+
+ case LDAP_FILTER_LE:
+ ad = f->f_av_desc;
+ if ( map_attr_value( dc, &ad, &atmp,
+ &f->f_av_value, &vtmp, RWM_MAP, op->o_tmpmemctx ) )
+ {
+ goto computed;
+ }
+
+ fstr->bv_len = atmp.bv_len + vtmp.bv_len + STRLENOF( "(<=)" );
+ fstr->bv_val = op->o_tmpalloc( fstr->bv_len + 1, op->o_tmpmemctx );
+
+ snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s<=%s)",
+ atmp.bv_val, vtmp.bv_len ? vtmp.bv_val : "" );
+
+ op->o_tmpfree( vtmp.bv_val, op->o_tmpmemctx );
+ break;
+
+ case LDAP_FILTER_APPROX:
+ ad = f->f_av_desc;
+ if ( map_attr_value( dc, &ad, &atmp,
+ &f->f_av_value, &vtmp, RWM_MAP, op->o_tmpmemctx ) )
+ {
+ goto computed;
+ }
+
+ fstr->bv_len = atmp.bv_len + vtmp.bv_len + STRLENOF( "(~=)" );
+ fstr->bv_val = op->o_tmpalloc( fstr->bv_len + 1, op->o_tmpmemctx );
+
+ snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s~=%s)",
+ atmp.bv_val, vtmp.bv_len ? vtmp.bv_val : "" );
+
+ op->o_tmpfree( vtmp.bv_val, op->o_tmpmemctx );
+ break;
+
+ case LDAP_FILTER_SUBSTRINGS:
+ ad = f->f_sub_desc;
+ if ( map_attr_value( dc, &ad, &atmp,
+ NULL, NULL, RWM_MAP, op->o_tmpmemctx ) )
+ {
+ goto computed;
+ }
+
+ /* cannot be a DN ... */
+
+ fstr->bv_len = atmp.bv_len + STRLENOF( "(=*)" );
+ fstr->bv_val = op->o_tmpalloc( fstr->bv_len + 128, op->o_tmpmemctx );
+
+ snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s=*)",
+ atmp.bv_val );
+
+ if ( !BER_BVISNULL( &f->f_sub_initial ) ) {
+ len = fstr->bv_len;
+
+ filter_escape_value_x( &f->f_sub_initial, &vtmp, op->o_tmpmemctx );
+
+ fstr->bv_len += vtmp.bv_len;
+ fstr->bv_val = op->o_tmprealloc( fstr->bv_val, fstr->bv_len + 1,
+ op->o_tmpmemctx );
+
+ snprintf( &fstr->bv_val[len - 2], vtmp.bv_len + 3,
+ /* "(attr=" */ "%s*)",
+ vtmp.bv_len ? vtmp.bv_val : "" );
+
+ op->o_tmpfree( vtmp.bv_val, op->o_tmpmemctx );
+ }
+
+ if ( f->f_sub_any != NULL ) {
+ for ( i = 0; !BER_BVISNULL( &f->f_sub_any[i] ); i++ ) {
+ len = fstr->bv_len;
+ filter_escape_value_x( &f->f_sub_any[i], &vtmp,
+ op->o_tmpmemctx );
+
+ fstr->bv_len += vtmp.bv_len + 1;
+ fstr->bv_val = op->o_tmprealloc( fstr->bv_val, fstr->bv_len + 1,
+ op->o_tmpmemctx );
+
+ snprintf( &fstr->bv_val[len - 1], vtmp.bv_len + 3,
+ /* "(attr=[init]*[any*]" */ "%s*)",
+ vtmp.bv_len ? vtmp.bv_val : "" );
+ op->o_tmpfree( vtmp.bv_val, op->o_tmpmemctx );
+ }
+ }
+
+ if ( !BER_BVISNULL( &f->f_sub_final ) ) {
+ len = fstr->bv_len;
+
+ filter_escape_value_x( &f->f_sub_final, &vtmp, op->o_tmpmemctx );
+
+ fstr->bv_len += vtmp.bv_len;
+ fstr->bv_val = op->o_tmprealloc( fstr->bv_val, fstr->bv_len + 1,
+ op->o_tmpmemctx );
+
+ snprintf( &fstr->bv_val[len - 1], vtmp.bv_len + 3,
+ /* "(attr=[init*][any*]" */ "%s)",
+ vtmp.bv_len ? vtmp.bv_val : "" );
+
+ op->o_tmpfree( vtmp.bv_val, op->o_tmpmemctx );
+ }
+
+ break;
+
+ case LDAP_FILTER_PRESENT:
+ ad = f->f_desc;
+ if ( map_attr_value( dc, &ad, &atmp,
+ NULL, NULL, RWM_MAP, op->o_tmpmemctx ) )
+ {
+ goto computed;
+ }
+
+ fstr->bv_len = atmp.bv_len + STRLENOF( "(=*)" );
+ fstr->bv_val = op->o_tmpalloc( fstr->bv_len + 1, op->o_tmpmemctx );
+
+ snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s=*)",
+ atmp.bv_val );
+ break;
+
+ case LDAP_FILTER_AND:
+ case LDAP_FILTER_OR:
+ case LDAP_FILTER_NOT:
+ fstr->bv_len = STRLENOF( "(%)" );
+ fstr->bv_val = op->o_tmpalloc( fstr->bv_len + 128, op->o_tmpmemctx );
+
+ snprintf( fstr->bv_val, fstr->bv_len + 1, "(%c)",
+ f->f_choice == LDAP_FILTER_AND ? '&' :
+ f->f_choice == LDAP_FILTER_OR ? '|' : '!' );
+
+ for ( p = f->f_list; p != NULL; p = p->f_next ) {
+ int rc;
+
+ len = fstr->bv_len;
+
+ rc = rwm_int_filter_map_rewrite( op, dc, p, &vtmp );
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+
+ fstr->bv_len += vtmp.bv_len;
+ fstr->bv_val = op->o_tmprealloc( fstr->bv_val, fstr->bv_len + 1,
+ op->o_tmpmemctx );
+
+ snprintf( &fstr->bv_val[len-1], vtmp.bv_len + 2,
+ /*"("*/ "%s)", vtmp.bv_len ? vtmp.bv_val : "" );
+
+ op->o_tmpfree( vtmp.bv_val, op->o_tmpmemctx );
+ }
+
+ break;
+
+ case LDAP_FILTER_EXT: {
+ if ( f->f_mr_desc ) {
+ ad = f->f_mr_desc;
+ if ( map_attr_value( dc, &ad, &atmp,
+ &f->f_mr_value, &vtmp, RWM_MAP, op->o_tmpmemctx ) )
+ {
+ goto computed;
+ }
+
+ } else {
+ BER_BVSTR( &atmp, "" );
+ filter_escape_value_x( &f->f_mr_value, &vtmp, op->o_tmpmemctx );
+ }
+
+
+ fstr->bv_len = atmp.bv_len +
+ ( f->f_mr_dnattrs ? STRLENOF( ":dn" ) : 0 ) +
+ ( f->f_mr_rule_text.bv_len ? f->f_mr_rule_text.bv_len + 1 : 0 ) +
+ vtmp.bv_len + STRLENOF( "(:=)" );
+ fstr->bv_val = op->o_tmpalloc( fstr->bv_len + 1, op->o_tmpmemctx );
+
+ snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s%s%s%s:=%s)",
+ atmp.bv_val,
+ f->f_mr_dnattrs ? ":dn" : "",
+ !BER_BVISEMPTY( &f->f_mr_rule_text ) ? ":" : "",
+ !BER_BVISEMPTY( &f->f_mr_rule_text ) ? f->f_mr_rule_text.bv_val : "",
+ vtmp.bv_len ? vtmp.bv_val : "" );
+ op->o_tmpfree( vtmp.bv_val, op->o_tmpmemctx );
+ break;
+ }
+
+ case -1:
+computed:;
+ filter_free_x( op, f, 0 );
+ f->f_choice = SLAPD_FILTER_COMPUTED;
+ f->f_result = SLAPD_COMPARE_UNDEFINED;
+ /* fallthru */
+
+ case SLAPD_FILTER_COMPUTED:
+ switch ( f->f_result ) {
+ case LDAP_COMPARE_FALSE:
+ /* FIXME: treat UNDEFINED as FALSE */
+ case SLAPD_COMPARE_UNDEFINED:
+ if ( dc->rwmap->rwm_flags & RWM_F_SUPPORT_T_F ) {
+ tmp = &ber_bvtf_false;
+ break;
+ }
+ tmp = &ber_bvfalse;
+ break;
+
+ case LDAP_COMPARE_TRUE:
+ if ( dc->rwmap->rwm_flags & RWM_F_SUPPORT_T_F ) {
+ tmp = &ber_bvtf_true;
+ break;
+ }
+ tmp = &ber_bvtrue;
+ break;
+
+ default:
+ tmp = &ber_bverror;
+ break;
+ }
+
+ ber_dupbv_x( fstr, tmp, op->o_tmpmemctx );
+ break;
+
+ default:
+ ber_dupbv_x( fstr, &ber_bvunknown, op->o_tmpmemctx );
+ break;
+ }
+
+ return LDAP_SUCCESS;
+}
+
+int
+rwm_filter_map_rewrite(
+ Operation *op,
+ dncookie *dc,
+ Filter *f,
+ struct berval *fstr )
+{
+ int rc;
+ dncookie fdc;
+ struct berval ftmp;
+
+ rc = rwm_int_filter_map_rewrite( op, dc, f, fstr );
+
+ if ( rc != 0 ) {
+ return rc;
+ }
+
+ fdc = *dc;
+ ftmp = *fstr;
+
+ fdc.ctx = "searchFilter";
+
+ switch ( rewrite_session( fdc.rwmap->rwm_rw, fdc.ctx,
+ ( !BER_BVISEMPTY( &ftmp ) ? ftmp.bv_val : "" ),
+ fdc.conn, &fstr->bv_val ) )
+ {
+ case REWRITE_REGEXEC_OK:
+ if ( !BER_BVISNULL( fstr ) ) {
+ fstr->bv_len = strlen( fstr->bv_val );
+
+ } else {
+ *fstr = ftmp;
+ }
+
+ Debug( LDAP_DEBUG_ARGS,
+ "[rw] %s: \"%s\" -> \"%s\"\n",
+ fdc.ctx, ftmp.bv_val, fstr->bv_val );
+ if ( fstr->bv_val != ftmp.bv_val ) {
+ ber_bvreplace_x( &ftmp, fstr, op->o_tmpmemctx );
+ ch_free( fstr->bv_val );
+ *fstr = ftmp;
+ }
+ rc = LDAP_SUCCESS;
+ break;
+
+ case REWRITE_REGEXEC_UNWILLING:
+ if ( fdc.rs ) {
+ fdc.rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ fdc.rs->sr_text = "Operation not allowed";
+ }
+ op->o_tmpfree( ftmp.bv_val, op->o_tmpmemctx );
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ break;
+
+ case REWRITE_REGEXEC_ERR:
+ if ( fdc.rs ) {
+ fdc.rs->sr_err = LDAP_OTHER;
+ fdc.rs->sr_text = "Rewrite error";
+ }
+ op->o_tmpfree( ftmp.bv_val, op->o_tmpmemctx );
+ rc = LDAP_OTHER;
+ break;
+ }
+
+ return rc;
+}
+
+/*
+ * I don't like this much, but we need two different
+ * functions because different heap managers may be
+ * in use in back-ldap/meta to reduce the amount of
+ * calls to malloc routines, and some of the free()
+ * routines may be macros with args
+ */
+int
+rwm_referral_rewrite(
+ Operation *op,
+ SlapReply *rs,
+ void *cookie,
+ BerVarray a_vals,
+ BerVarray *pa_nvals )
+{
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ struct ldaprwmap *rwmap =
+ (struct ldaprwmap *)on->on_bi.bi_private;
+
+ int i, last;
+
+ dncookie dc;
+ struct berval dn = BER_BVNULL,
+ ndn = BER_BVNULL;
+
+ assert( a_vals != NULL );
+
+ /*
+ * Rewrite the dn if needed
+ */
+ dc.rwmap = rwmap;
+ dc.conn = op->o_conn;
+ dc.rs = rs;
+ dc.ctx = (char *)cookie;
+
+ for ( last = 0; !BER_BVISNULL( &a_vals[last] ); last++ )
+ ;
+ last--;
+
+ if ( pa_nvals != NULL ) {
+ if ( *pa_nvals == NULL ) {
+ *pa_nvals = ch_malloc( ( last + 2 ) * sizeof(struct berval) );
+ memset( *pa_nvals, 0, ( last + 2 ) * sizeof(struct berval) );
+ }
+ }
+
+ for ( i = 0; !BER_BVISNULL( &a_vals[i] ); i++ ) {
+ struct berval olddn = BER_BVNULL,
+ oldval;
+ int rc;
+ LDAPURLDesc *ludp;
+
+ oldval = a_vals[i];
+ rc = ldap_url_parse( oldval.bv_val, &ludp );
+ if ( rc != LDAP_URL_SUCCESS ) {
+ /* leave attr untouched if massage failed */
+ if ( pa_nvals && BER_BVISNULL( &(*pa_nvals)[i] ) ) {
+ ber_dupbv( &(*pa_nvals)[i], &oldval );
+ }
+ continue;
+ }
+
+ /* FIXME: URLs like "ldap:///dc=suffix" if passed
+ * thru ldap_url_parse() and ldap_url_desc2str()
+ * get rewritten as "ldap:///dc=suffix??base";
+ * we don't want this to occur... */
+ if ( ludp->lud_scope == LDAP_SCOPE_BASE ) {
+ ludp->lud_scope = LDAP_SCOPE_DEFAULT;
+ }
+
+ ber_str2bv( ludp->lud_dn, 0, 0, &olddn );
+
+ dn = olddn;
+ if ( pa_nvals ) {
+ ndn = olddn;
+ rc = rwm_dn_massage_pretty_normalize( &dc, &olddn,
+ &dn, &ndn );
+ } else {
+ rc = rwm_dn_massage_pretty( &dc, &olddn, &dn );
+ }
+
+ switch ( rc ) {
+ case LDAP_UNWILLING_TO_PERFORM:
+ /*
+ * FIXME: need to check if it may be considered
+ * legal to trim values when adding/modifying;
+ * it should be when searching (e.g. ACLs).
+ */
+ ch_free( a_vals[i].bv_val );
+ if (last > i ) {
+ a_vals[i] = a_vals[last];
+ if ( pa_nvals ) {
+ (*pa_nvals)[i] = (*pa_nvals)[last];
+ }
+ }
+ BER_BVZERO( &a_vals[last] );
+ if ( pa_nvals ) {
+ BER_BVZERO( &(*pa_nvals)[last] );
+ }
+ last--;
+ break;
+
+ case LDAP_SUCCESS:
+ if ( !BER_BVISNULL( &dn ) && dn.bv_val != olddn.bv_val ) {
+ char *newurl;
+
+ ludp->lud_dn = dn.bv_val;
+ newurl = ldap_url_desc2str( ludp );
+ ludp->lud_dn = olddn.bv_val;
+ ch_free( dn.bv_val );
+ if ( newurl == NULL ) {
+ /* FIXME: leave attr untouched
+ * even if ldap_url_desc2str failed...
+ */
+ break;
+ }
+
+ ber_str2bv( newurl, 0, 1, &a_vals[i] );
+ LDAP_FREE( newurl );
+
+ if ( pa_nvals ) {
+ ludp->lud_dn = ndn.bv_val;
+ newurl = ldap_url_desc2str( ludp );
+ ludp->lud_dn = olddn.bv_val;
+ ch_free( ndn.bv_val );
+ if ( newurl == NULL ) {
+ /* FIXME: leave attr untouched
+ * even if ldap_url_desc2str failed...
+ */
+ ch_free( a_vals[i].bv_val );
+ a_vals[i] = oldval;
+ break;
+ }
+
+ if ( !BER_BVISNULL( &(*pa_nvals)[i] ) ) {
+ ch_free( (*pa_nvals)[i].bv_val );
+ }
+ ber_str2bv( newurl, 0, 1, &(*pa_nvals)[i] );
+ LDAP_FREE( newurl );
+ }
+
+ ch_free( oldval.bv_val );
+ ludp->lud_dn = olddn.bv_val;
+ }
+ break;
+
+ default:
+ /* leave attr untouched if massage failed */
+ if ( pa_nvals && BER_BVISNULL( &(*pa_nvals)[i] ) ) {
+ ber_dupbv( &(*pa_nvals)[i], &a_vals[i] );
+ }
+ break;
+ }
+ ldap_free_urldesc( ludp );
+ }
+
+ return 0;
+}
+
+/*
+ * I don't like this much, but we need two different
+ * functions because different heap managers may be
+ * in use in back-ldap/meta to reduce the amount of
+ * calls to malloc routines, and some of the free()
+ * routines may be macros with args
+ */
+int
+rwm_dnattr_rewrite(
+ Operation *op,
+ SlapReply *rs,
+ void *cookie,
+ BerVarray a_vals,
+ BerVarray *pa_nvals )
+{
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ struct ldaprwmap *rwmap =
+ (struct ldaprwmap *)on->on_bi.bi_private;
+
+ int i, last;
+
+ dncookie dc;
+ struct berval dn = BER_BVNULL,
+ ndn = BER_BVNULL;
+ BerVarray in;
+
+ if ( a_vals ) {
+ in = a_vals;
+
+ } else {
+ if ( pa_nvals == NULL || *pa_nvals == NULL ) {
+ return LDAP_OTHER;
+ }
+ in = *pa_nvals;
+ }
+
+ /*
+ * Rewrite the dn if needed
+ */
+ dc.rwmap = rwmap;
+ dc.conn = op->o_conn;
+ dc.rs = rs;
+ dc.ctx = (char *)cookie;
+
+ for ( last = 0; !BER_BVISNULL( &in[last] ); last++ );
+ last--;
+ if ( pa_nvals != NULL ) {
+ if ( *pa_nvals == NULL ) {
+ *pa_nvals = ch_malloc( ( last + 2 ) * sizeof(struct berval) );
+ memset( *pa_nvals, 0, ( last + 2 ) * sizeof(struct berval) );
+ }
+ }
+
+ for ( i = 0; !BER_BVISNULL( &in[i] ); i++ ) {
+ int rc;
+
+ if ( a_vals ) {
+ dn = in[i];
+ if ( pa_nvals ) {
+ ndn = (*pa_nvals)[i];
+ rc = rwm_dn_massage_pretty_normalize( &dc, &in[i], &dn, &ndn );
+ } else {
+ rc = rwm_dn_massage_pretty( &dc, &in[i], &dn );
+ }
+ } else {
+ ndn = in[i];
+ rc = rwm_dn_massage_normalize( &dc, &in[i], &ndn );
+ }
+
+ switch ( rc ) {
+ case LDAP_UNWILLING_TO_PERFORM:
+ /*
+ * FIXME: need to check if it may be considered
+ * legal to trim values when adding/modifying;
+ * it should be when searching (e.g. ACLs).
+ */
+ ch_free( in[i].bv_val );
+ if (last > i ) {
+ in[i] = in[last];
+ if ( a_vals && pa_nvals ) {
+ (*pa_nvals)[i] = (*pa_nvals)[last];
+ }
+ }
+ BER_BVZERO( &in[last] );
+ if ( a_vals && pa_nvals ) {
+ BER_BVZERO( &(*pa_nvals)[last] );
+ }
+ last--;
+ break;
+
+ case LDAP_SUCCESS:
+ if ( a_vals ) {
+ if ( !BER_BVISNULL( &dn ) && dn.bv_val != a_vals[i].bv_val ) {
+ ch_free( a_vals[i].bv_val );
+ a_vals[i] = dn;
+
+ if ( pa_nvals ) {
+ if ( !BER_BVISNULL( &(*pa_nvals)[i] ) ) {
+ ch_free( (*pa_nvals)[i].bv_val );
+ }
+ (*pa_nvals)[i] = ndn;
+ }
+ }
+
+ } else {
+ if ( !BER_BVISNULL( &ndn ) && ndn.bv_val != (*pa_nvals)[i].bv_val ) {
+ ch_free( (*pa_nvals)[i].bv_val );
+ (*pa_nvals)[i] = ndn;
+ }
+ }
+ break;
+
+ default:
+ /* leave attr untouched if massage failed */
+ if ( a_vals && pa_nvals && BER_BVISNULL( &(*pa_nvals)[i] ) ) {
+ dnNormalize( 0, NULL, NULL, &a_vals[i], &(*pa_nvals)[i], NULL );
+ }
+ break;
+ }
+ }
+
+ return 0;
+}
+
+int
+rwm_referral_result_rewrite(
+ dncookie *dc,
+ BerVarray a_vals )
+{
+ int i, last;
+
+ for ( last = 0; !BER_BVISNULL( &a_vals[last] ); last++ );
+ last--;
+
+ for ( i = 0; !BER_BVISNULL( &a_vals[i] ); i++ ) {
+ struct berval dn,
+ olddn = BER_BVNULL;
+ int rc;
+ LDAPURLDesc *ludp;
+
+ rc = ldap_url_parse( a_vals[i].bv_val, &ludp );
+ if ( rc != LDAP_URL_SUCCESS ) {
+ /* leave attr untouched if massage failed */
+ continue;
+ }
+
+ /* FIXME: URLs like "ldap:///dc=suffix" if passed
+ * thru ldap_url_parse() and ldap_url_desc2str()
+ * get rewritten as "ldap:///dc=suffix??base";
+ * we don't want this to occur... */
+ if ( ludp->lud_scope == LDAP_SCOPE_BASE ) {
+ ludp->lud_scope = LDAP_SCOPE_DEFAULT;
+ }
+
+ ber_str2bv( ludp->lud_dn, 0, 0, &olddn );
+
+ dn = olddn;
+ rc = rwm_dn_massage_pretty( dc, &olddn, &dn );
+ switch ( rc ) {
+ case LDAP_UNWILLING_TO_PERFORM:
+ /*
+ * FIXME: need to check if it may be considered
+ * legal to trim values when adding/modifying;
+ * it should be when searching (e.g. ACLs).
+ */
+ ch_free( a_vals[i].bv_val );
+ if ( last > i ) {
+ a_vals[i] = a_vals[last];
+ }
+ BER_BVZERO( &a_vals[last] );
+ last--;
+ i--;
+ break;
+
+ default:
+ /* leave attr untouched if massage failed */
+ if ( !BER_BVISNULL( &dn ) && olddn.bv_val != dn.bv_val ) {
+ char *newurl;
+
+ ludp->lud_dn = dn.bv_val;
+ newurl = ldap_url_desc2str( ludp );
+ if ( newurl == NULL ) {
+ /* FIXME: leave attr untouched
+ * even if ldap_url_desc2str failed...
+ */
+ break;
+ }
+
+ ch_free( a_vals[i].bv_val );
+ ber_str2bv( newurl, 0, 1, &a_vals[i] );
+ LDAP_FREE( newurl );
+ ludp->lud_dn = olddn.bv_val;
+ }
+ break;
+ }
+
+ ldap_free_urldesc( ludp );
+ }
+
+ return 0;
+}
+
+int
+rwm_dnattr_result_rewrite(
+ dncookie *dc,
+ BerVarray a_vals,
+ BerVarray a_nvals )
+{
+ int i, last;
+
+ for ( last = 0; !BER_BVISNULL( &a_vals[last] ); last++ );
+ last--;
+
+ for ( i = 0; !BER_BVISNULL( &a_vals[i] ); i++ ) {
+ struct berval pdn, ndn = BER_BVNULL;
+ int rc;
+
+ pdn = a_vals[i];
+ rc = rwm_dn_massage_pretty_normalize( dc, &a_vals[i], &pdn, &ndn );
+ switch ( rc ) {
+ case LDAP_UNWILLING_TO_PERFORM:
+ /*
+ * FIXME: need to check if it may be considered
+ * legal to trim values when adding/modifying;
+ * it should be when searching (e.g. ACLs).
+ */
+ assert( a_vals[i].bv_val != a_nvals[i].bv_val );
+ ch_free( a_vals[i].bv_val );
+ ch_free( a_nvals[i].bv_val );
+ if ( last > i ) {
+ a_vals[i] = a_vals[last];
+ a_nvals[i] = a_nvals[last];
+ }
+ BER_BVZERO( &a_vals[last] );
+ BER_BVZERO( &a_nvals[last] );
+ last--;
+ break;
+
+ default:
+ /* leave attr untouched if massage failed */
+ if ( !BER_BVISNULL( &pdn ) && a_vals[i].bv_val != pdn.bv_val ) {
+ ch_free( a_vals[i].bv_val );
+ a_vals[i] = pdn;
+ }
+ if ( !BER_BVISNULL( &ndn ) && a_nvals[i].bv_val != ndn.bv_val ) {
+ ch_free( a_nvals[i].bv_val );
+ a_nvals[i] = ndn;
+ }
+ break;
+ }
+ }
+
+ return 0;
+}
+
+void
+rwm_mapping_dst_free( void *v_mapping )
+{
+ struct ldapmapping *mapping = v_mapping;
+
+ if ( BER_BVISEMPTY( &mapping[0].m_dst ) ) {
+ rwm_mapping_free( &mapping[ -1 ] );
+ }
+}
+
+void
+rwm_mapping_free( void *v_mapping )
+{
+ struct ldapmapping *mapping = v_mapping;
+
+ if ( !BER_BVISNULL( &mapping[0].m_src ) ) {
+ ch_free( mapping[0].m_src.bv_val );
+ }
+
+ if ( mapping[0].m_flags & RWMMAP_F_FREE_SRC ) {
+ if ( mapping[0].m_flags & RWMMAP_F_IS_OC ) {
+ if ( mapping[0].m_src_oc ) {
+ ch_free( mapping[0].m_src_oc );
+ }
+
+ } else {
+ if ( mapping[0].m_src_ad ) {
+ ch_free( mapping[0].m_src_ad );
+ }
+ }
+ }
+
+ if ( !BER_BVISNULL( &mapping[0].m_dst ) ) {
+ ch_free( mapping[0].m_dst.bv_val );
+ }
+
+ if ( mapping[0].m_flags & RWMMAP_F_FREE_DST ) {
+ if ( mapping[0].m_flags & RWMMAP_F_IS_OC ) {
+ if ( mapping[0].m_dst_oc ) {
+ ch_free( mapping[0].m_dst_oc );
+ }
+
+ } else {
+ if ( mapping[0].m_dst_ad ) {
+ ch_free( mapping[0].m_dst_ad );
+ }
+ }
+ }
+
+ ch_free( mapping );
+
+}
+
+#endif /* SLAPD_OVER_RWM */
diff --git a/translucent.c b/translucent.c
new file mode 100644
index 0000000..4b4ddb8
--- /dev/null
+++ b/translucent.c
@@ -0,0 +1,1434 @@
+/* translucent.c - translucent proxy module */
+/* $OpenLDAP: pkg/ldap/servers/slapd/overlays/translucent.c,v 1.13.2.35 2010/04/15 20:02:30 quanah Exp $ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2004-2010 The OpenLDAP Foundation.
+ * Portions Copyright 2005 Symas Corporation.
+ * 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 Symas Corp. for inclusion in
+ * OpenLDAP Software. This work was sponsored by Hewlett-Packard.
+ */
+
+#include "portable.h"
+
+#ifdef SLAPD_OVER_TRANSLUCENT
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "lutil.h"
+
+#include "config.h"
+
+/* config block */
+typedef struct translucent_info {
+ BackendDB db; /* captive backend */
+ AttributeName *local; /* valid attrs for local filters */
+ AttributeName *remote; /* valid attrs for remote filters */
+ int strict;
+ int no_glue;
+ int defer_db_open;
+ int bind_local;
+ int pwmod_local;
+} translucent_info;
+
+static ConfigLDAPadd translucent_ldadd;
+static ConfigCfAdd translucent_cfadd;
+
+static ConfigDriver translucent_cf_gen;
+
+enum {
+ TRANS_LOCAL = 1,
+ TRANS_REMOTE
+};
+
+static ConfigTable translucentcfg[] = {
+ { "translucent_strict", "on|off", 1, 2, 0,
+ ARG_ON_OFF|ARG_OFFSET,
+ (void *)offsetof(translucent_info, strict),
+ "( OLcfgOvAt:14.1 NAME 'olcTranslucentStrict' "
+ "DESC 'Reveal attribute deletion constraint violations' "
+ "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
+ { "translucent_no_glue", "on|off", 1, 2, 0,
+ ARG_ON_OFF|ARG_OFFSET,
+ (void *)offsetof(translucent_info, no_glue),
+ "( OLcfgOvAt:14.2 NAME 'olcTranslucentNoGlue' "
+ "DESC 'Disable automatic glue records for ADD and MODRDN' "
+ "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
+ { "translucent_local", "attr[,attr...]", 1, 2, 0,
+ ARG_MAGIC|TRANS_LOCAL,
+ translucent_cf_gen,
+ "( OLcfgOvAt:14.3 NAME 'olcTranslucentLocal' "
+ "DESC 'Attributes to use in local search filter' "
+ "SYNTAX OMsDirectoryString )", NULL, NULL },
+ { "translucent_remote", "attr[,attr...]", 1, 2, 0,
+ ARG_MAGIC|TRANS_REMOTE,
+ translucent_cf_gen,
+ "( OLcfgOvAt:14.4 NAME 'olcTranslucentRemote' "
+ "DESC 'Attributes to use in remote search filter' "
+ "SYNTAX OMsDirectoryString )", NULL, NULL },
+ { "translucent_bind_local", "on|off", 1, 2, 0,
+ ARG_ON_OFF|ARG_OFFSET,
+ (void *)offsetof(translucent_info, bind_local),
+ "( OLcfgOvAt:14.5 NAME 'olcTranslucentBindLocal' "
+ "DESC 'Enable local bind' "
+ "SYNTAX OMsBoolean SINGLE-VALUE)", NULL, NULL },
+ { "translucent_pwmod_local", "on|off", 1, 2, 0,
+ ARG_ON_OFF|ARG_OFFSET,
+ (void *)offsetof(translucent_info, pwmod_local),
+ "( OLcfgOvAt:14.6 NAME 'olcTranslucentPwModLocal' "
+ "DESC 'Enable local RFC 3062 Password Modify extended operation' "
+ "SYNTAX OMsBoolean SINGLE-VALUE)", NULL, NULL },
+ { NULL, NULL, 0, 0, 0, ARG_IGNORED }
+};
+
+static ConfigOCs translucentocs[] = {
+ { "( OLcfgOvOc:14.1 "
+ "NAME 'olcTranslucentConfig' "
+ "DESC 'Translucent configuration' "
+ "SUP olcOverlayConfig "
+ "MAY ( olcTranslucentStrict $ olcTranslucentNoGlue $"
+ " olcTranslucentLocal $ olcTranslucentRemote $"
+ " olcTranslucentBindLocal $ olcTranslucentPwModLocal ) )",
+ Cft_Overlay, translucentcfg, NULL, translucent_cfadd },
+ { "( OLcfgOvOc:14.2 "
+ "NAME 'olcTranslucentDatabase' "
+ "DESC 'Translucent target database configuration' "
+ "AUXILIARY )", Cft_Misc, olcDatabaseDummy, translucent_ldadd },
+ { NULL, 0, NULL }
+};
+/* for translucent_init() */
+
+static int
+translucent_ldadd_cleanup( ConfigArgs *ca )
+{
+ slap_overinst *on = ca->ca_private;
+ translucent_info *ov = on->on_bi.bi_private;
+
+ ov->defer_db_open = 0;
+ return backend_startup_one( ca->be, &ca->reply );
+}
+
+static int
+translucent_ldadd( CfEntryInfo *cei, Entry *e, ConfigArgs *ca )
+{
+ slap_overinst *on;
+ translucent_info *ov;
+
+ Debug(LDAP_DEBUG_TRACE, "==> translucent_ldadd\n", 0, 0, 0);
+
+ if ( cei->ce_type != Cft_Overlay || !cei->ce_bi ||
+ cei->ce_bi->bi_cf_ocs != translucentocs )
+ return LDAP_CONSTRAINT_VIOLATION;
+
+ on = (slap_overinst *)cei->ce_bi;
+ ov = on->on_bi.bi_private;
+ ca->be = &ov->db;
+ ca->ca_private = on;
+ if ( CONFIG_ONLINE_ADD( ca ))
+ ca->cleanup = translucent_ldadd_cleanup;
+ else
+ ov->defer_db_open = 0;
+
+ return LDAP_SUCCESS;
+}
+
+static int
+translucent_cfadd( Operation *op, SlapReply *rs, Entry *e, ConfigArgs *ca )
+{
+ CfEntryInfo *cei = e->e_private;
+ slap_overinst *on = (slap_overinst *)cei->ce_bi;
+ translucent_info *ov = on->on_bi.bi_private;
+ struct berval bv;
+
+ Debug(LDAP_DEBUG_TRACE, "==> translucent_cfadd\n", 0, 0, 0);
+
+ /* FIXME: should not hardcode "olcDatabase" here */
+ bv.bv_len = snprintf( ca->cr_msg, sizeof( ca->cr_msg ),
+ "olcDatabase=" SLAP_X_ORDERED_FMT "%s",
+ 0, ov->db.bd_info->bi_type );
+ if ( bv.bv_len >= sizeof( ca->cr_msg ) ) {
+ return -1;
+ }
+ bv.bv_val = ca->cr_msg;
+ ca->be = &ov->db;
+ ov->defer_db_open = 0;
+
+ /* We can only create this entry if the database is table-driven
+ */
+ if ( ov->db.bd_info->bi_cf_ocs )
+ config_build_entry( op, rs, cei, ca, &bv,
+ ov->db.bd_info->bi_cf_ocs,
+ &translucentocs[1] );
+
+ return 0;
+}
+
+static int
+translucent_cf_gen( ConfigArgs *c )
+{
+ slap_overinst *on = (slap_overinst *)c->bi;
+ translucent_info *ov = on->on_bi.bi_private;
+ AttributeName **an, *a2;
+ int i;
+
+ if ( c->type == TRANS_LOCAL )
+ an = &ov->local;
+ else
+ an = &ov->remote;
+
+ if ( c->op == SLAP_CONFIG_EMIT ) {
+ if ( !*an )
+ return 1;
+ for ( i = 0; !BER_BVISNULL(&(*an)[i].an_name); i++ ) {
+ value_add_one( &c->rvalue_vals, &(*an)[i].an_name );
+ }
+ return ( i < 1 );
+ } else if ( c->op == LDAP_MOD_DELETE ) {
+ if ( c->valx < 0 ) {
+ anlist_free( *an, 1, NULL );
+ *an = NULL;
+ } else {
+ i = c->valx;
+ ch_free( (*an)[i].an_name.bv_val );
+ do {
+ (*an)[i] = (*an)[i+1];
+ i++;
+ } while ( !BER_BVISNULL( &(*an)[i].an_name ));
+ }
+ return 0;
+ }
+ a2 = str2anlist( *an, c->argv[1], "," );
+ if ( !a2 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s unable to parse attribute %s",
+ c->argv[0], c->argv[1] );
+ Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
+ "%s: %s\n", c->log, c->cr_msg, 0 );
+ return ARG_BAD_CONF;
+ }
+ *an = a2;
+ return 0;
+}
+
+static slap_overinst translucent;
+
+/*
+** glue_parent()
+** call syncrepl_add_glue() with the parent suffix;
+**
+*/
+
+static struct berval glue[] = { BER_BVC("top"), BER_BVC("glue"), BER_BVNULL };
+
+void glue_parent(Operation *op) {
+ Operation nop = *op;
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ struct berval ndn = BER_BVNULL;
+ Attribute *a;
+ Entry *e;
+ struct berval pdn;
+
+ dnParent( &op->o_req_ndn, &pdn );
+ ber_dupbv_x( &ndn, &pdn, op->o_tmpmemctx );
+
+ Debug(LDAP_DEBUG_TRACE, "=> glue_parent: fabricating glue for <%s>\n", ndn.bv_val, 0, 0);
+
+ e = entry_alloc();
+ e->e_id = NOID;
+ ber_dupbv(&e->e_name, &ndn);
+ ber_dupbv(&e->e_nname, &ndn);
+
+ a = attr_alloc( slap_schema.si_ad_objectClass );
+ a->a_numvals = 2;
+ a->a_vals = ch_malloc(sizeof(struct berval) * 3);
+ ber_dupbv(&a->a_vals[0], &glue[0]);
+ ber_dupbv(&a->a_vals[1], &glue[1]);
+ ber_dupbv(&a->a_vals[2], &glue[2]);
+ a->a_nvals = a->a_vals;
+ a->a_next = e->e_attrs;
+ e->e_attrs = a;
+
+ a = attr_alloc( slap_schema.si_ad_structuralObjectClass );
+ a->a_numvals = 1;
+ a->a_vals = ch_malloc(sizeof(struct berval) * 2);
+ ber_dupbv(&a->a_vals[0], &glue[1]);
+ ber_dupbv(&a->a_vals[1], &glue[2]);
+ a->a_nvals = a->a_vals;
+ a->a_next = e->e_attrs;
+ e->e_attrs = a;
+
+ nop.o_req_dn = ndn;
+ nop.o_req_ndn = ndn;
+ nop.ora_e = e;
+
+ nop.o_bd->bd_info = (BackendInfo *) on->on_info->oi_orig;
+ syncrepl_add_glue(&nop, e);
+ nop.o_bd->bd_info = (BackendInfo *) on;
+
+ op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx );
+
+ return;
+}
+
+/*
+** free_attr_chain()
+** free only the Attribute*, not the contents;
+**
+*/
+void free_attr_chain(Attribute *b) {
+ Attribute *a;
+ for(a=b; a; a=a->a_next) {
+ a->a_vals = NULL;
+ a->a_nvals = NULL;
+ }
+ attrs_free( b );
+ return;
+}
+
+/*
+** translucent_add()
+** if not bound as root, send ACCESS error;
+** if glue, glue_parent();
+** return CONTINUE;
+**
+*/
+
+static int translucent_add(Operation *op, SlapReply *rs) {
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ translucent_info *ov = on->on_bi.bi_private;
+ Debug(LDAP_DEBUG_TRACE, "==> translucent_add: %s\n",
+ op->o_req_dn.bv_val, 0, 0);
+ if(!be_isroot(op)) {
+ op->o_bd->bd_info = (BackendInfo *) on->on_info;
+ send_ldap_error(op, rs, LDAP_INSUFFICIENT_ACCESS,
+ "user modification of overlay database not permitted");
+ op->o_bd->bd_info = (BackendInfo *) on;
+ return(rs->sr_err);
+ }
+ if(!ov->no_glue) glue_parent(op);
+ return(SLAP_CB_CONTINUE);
+}
+
+/*
+** translucent_modrdn()
+** if not bound as root, send ACCESS error;
+** if !glue, glue_parent();
+** else return CONTINUE;
+**
+*/
+
+static int translucent_modrdn(Operation *op, SlapReply *rs) {
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ translucent_info *ov = on->on_bi.bi_private;
+ Debug(LDAP_DEBUG_TRACE, "==> translucent_modrdn: %s -> %s\n",
+ op->o_req_dn.bv_val, op->orr_newrdn.bv_val, 0);
+ if(!be_isroot(op)) {
+ op->o_bd->bd_info = (BackendInfo *) on->on_info;
+ send_ldap_error(op, rs, LDAP_INSUFFICIENT_ACCESS,
+ "user modification of overlay database not permitted");
+ op->o_bd->bd_info = (BackendInfo *) on;
+ return(rs->sr_err);
+ }
+ if(!ov->no_glue) {
+ op->o_tag = LDAP_REQ_ADD;
+ glue_parent(op);
+ op->o_tag = LDAP_REQ_MODRDN;
+ }
+ return(SLAP_CB_CONTINUE);
+}
+
+/*
+** translucent_delete()
+** if not bound as root, send ACCESS error;
+** else return CONTINUE;
+**
+*/
+
+static int translucent_delete(Operation *op, SlapReply *rs) {
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ Debug(LDAP_DEBUG_TRACE, "==> translucent_delete: %s\n",
+ op->o_req_dn.bv_val, 0, 0);
+ if(!be_isroot(op)) {
+ op->o_bd->bd_info = (BackendInfo *) on->on_info;
+ send_ldap_error(op, rs, LDAP_INSUFFICIENT_ACCESS,
+ "user modification of overlay database not permitted");
+ op->o_bd->bd_info = (BackendInfo *) on;
+ return(rs->sr_err);
+ }
+ return(SLAP_CB_CONTINUE);
+}
+
+static int
+translucent_tag_cb( Operation *op, SlapReply *rs )
+{
+ op->o_tag = LDAP_REQ_MODIFY;
+ op->orm_modlist = op->o_callback->sc_private;
+ rs->sr_tag = slap_req2res( op->o_tag );
+
+ return SLAP_CB_CONTINUE;
+}
+
+/*
+** translucent_modify()
+** modify in local backend if exists in both;
+** otherwise, add to local backend;
+** fail if not defined in captive backend;
+**
+*/
+
+static int translucent_modify(Operation *op, SlapReply *rs) {
+ SlapReply nrs = { REP_RESULT };
+
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ translucent_info *ov = on->on_bi.bi_private;
+ Entry *e = NULL, *re = NULL;
+ Attribute *a, *ax;
+ Modifications *m, **mm;
+ BackendDB *db;
+ int del, rc, erc = 0;
+ slap_callback cb = { 0 };
+
+ Debug(LDAP_DEBUG_TRACE, "==> translucent_modify: %s\n",
+ op->o_req_dn.bv_val, 0, 0);
+
+ if(ov->defer_db_open) {
+ send_ldap_error(op, rs, LDAP_UNAVAILABLE,
+ "remote DB not available");
+ return(rs->sr_err);
+ }
+/*
+** fetch entry from the captive backend;
+** if it did not exist, fail;
+** release it, if captive backend supports this;
+**
+*/
+
+ db = op->o_bd;
+ op->o_bd = &ov->db;
+ ov->db.be_acl = op->o_bd->be_acl;
+ rc = ov->db.bd_info->bi_entry_get_rw(op, &op->o_req_ndn, NULL, NULL, 0, &re);
+ if(rc != LDAP_SUCCESS || re == NULL ) {
+ send_ldap_error((op), rs, LDAP_NO_SUCH_OBJECT,
+ "attempt to modify nonexistent local record");
+ return(rs->sr_err);
+ }
+ op->o_bd = db;
+/*
+** fetch entry from local backend;
+** if it exists:
+** foreach Modification:
+** if attr not present in local:
+** if Mod == LDAP_MOD_DELETE:
+** if remote attr not present, return NO_SUCH;
+** if remote attr present, drop this Mod;
+** else force this Mod to LDAP_MOD_ADD;
+** return CONTINUE;
+**
+*/
+
+ op->o_bd->bd_info = (BackendInfo *) on->on_info;
+ rc = be_entry_get_rw(op, &op->o_req_ndn, NULL, NULL, 0, &e);
+ op->o_bd->bd_info = (BackendInfo *) on;
+
+ if(e && rc == LDAP_SUCCESS) {
+ Debug(LDAP_DEBUG_TRACE, "=> translucent_modify: found local entry\n", 0, 0, 0);
+ for(mm = &op->orm_modlist; *mm; ) {
+ m = *mm;
+ for(a = e->e_attrs; a; a = a->a_next)
+ if(a->a_desc == m->sml_desc) break;
+ if(a) {
+ mm = &m->sml_next;
+ continue; /* found local attr */
+ }
+ if(m->sml_op == LDAP_MOD_DELETE) {
+ for(a = re->e_attrs; a; a = a->a_next)
+ if(a->a_desc == m->sml_desc) break;
+ /* not found remote attr */
+ if(!a) {
+ erc = LDAP_NO_SUCH_ATTRIBUTE;
+ goto release;
+ }
+ if(ov->strict) {
+ erc = LDAP_CONSTRAINT_VIOLATION;
+ goto release;
+ }
+ Debug(LDAP_DEBUG_TRACE,
+ "=> translucent_modify: silently dropping delete: %s\n",
+ m->sml_desc->ad_cname.bv_val, 0, 0);
+ *mm = m->sml_next;
+ m->sml_next = NULL;
+ slap_mods_free(m, 1);
+ continue;
+ }
+ m->sml_op = LDAP_MOD_ADD;
+ mm = &m->sml_next;
+ }
+ erc = SLAP_CB_CONTINUE;
+release:
+ if(re) {
+ if(ov->db.bd_info->bi_entry_release_rw) {
+ op->o_bd = &ov->db;
+ ov->db.bd_info->bi_entry_release_rw(op, re, 0);
+ op->o_bd = db;
+ } else
+ entry_free(re);
+ }
+ op->o_bd->bd_info = (BackendInfo *) on->on_info;
+ be_entry_release_r(op, e);
+ op->o_bd->bd_info = (BackendInfo *) on;
+ if(erc == SLAP_CB_CONTINUE) {
+ return(erc);
+ } else if(erc) {
+ send_ldap_error(op, rs, erc,
+ "attempt to delete nonexistent attribute");
+ return(erc);
+ }
+ }
+
+ /* don't leak remote entry copy */
+ if(re) {
+ if(ov->db.bd_info->bi_entry_release_rw) {
+ op->o_bd = &ov->db;
+ ov->db.bd_info->bi_entry_release_rw(op, re, 0);
+ op->o_bd = db;
+ } else
+ entry_free(re);
+ }
+/*
+** foreach Modification:
+** if MOD_ADD or MOD_REPLACE, add Attribute;
+** if no Modifications were suitable:
+** if strict, throw CONSTRAINT_VIOLATION;
+** else, return early SUCCESS;
+** fabricate Entry with new Attribute chain;
+** glue_parent() for this Entry;
+** call bi_op_add() in local backend;
+**
+*/
+
+ Debug(LDAP_DEBUG_TRACE, "=> translucent_modify: fabricating local add\n", 0, 0, 0);
+ a = NULL;
+ for(del = 0, ax = NULL, m = op->orm_modlist; m; m = m->sml_next) {
+ Attribute atmp;
+ if(((m->sml_op & LDAP_MOD_OP) != LDAP_MOD_ADD) &&
+ ((m->sml_op & LDAP_MOD_OP) != LDAP_MOD_REPLACE)) {
+ Debug(LDAP_DEBUG_ANY,
+ "=> translucent_modify: silently dropped modification(%d): %s\n",
+ m->sml_op, m->sml_desc->ad_cname.bv_val, 0);
+ if((m->sml_op & LDAP_MOD_OP) == LDAP_MOD_DELETE) del++;
+ continue;
+ }
+ atmp.a_desc = m->sml_desc;
+ atmp.a_vals = m->sml_values;
+ atmp.a_nvals = m->sml_nvalues ? m->sml_nvalues : atmp.a_vals;
+ atmp.a_numvals = m->sml_numvals;
+ atmp.a_flags = 0;
+ a = attr_dup( &atmp );
+ a->a_next = ax;
+ ax = a;
+ }
+
+ if(del && ov->strict) {
+ attrs_free( a );
+ send_ldap_error(op, rs, LDAP_CONSTRAINT_VIOLATION,
+ "attempt to delete attributes from local database");
+ return(rs->sr_err);
+ }
+
+ if(!ax) {
+ if(ov->strict) {
+ send_ldap_error(op, rs, LDAP_CONSTRAINT_VIOLATION,
+ "modification contained other than ADD or REPLACE");
+ return(rs->sr_err);
+ }
+ /* rs->sr_text = "no valid modification found"; */
+ rs->sr_err = LDAP_SUCCESS;
+ send_ldap_result(op, rs);
+ return(rs->sr_err);
+ }
+
+ e = entry_alloc();
+ ber_dupbv( &e->e_name, &op->o_req_dn );
+ ber_dupbv( &e->e_nname, &op->o_req_ndn );
+ e->e_attrs = a;
+
+ op->o_tag = LDAP_REQ_ADD;
+ cb.sc_response = translucent_tag_cb;
+ cb.sc_private = op->orm_modlist;
+ op->oq_add.rs_e = e;
+
+ glue_parent(op);
+
+ cb.sc_next = op->o_callback;
+ op->o_callback = &cb;
+ rc = on->on_info->oi_orig->bi_op_add(op, &nrs);
+ if ( op->ora_e == e )
+ entry_free( e );
+ op->o_callback = cb.sc_next;
+
+ return(rc);
+}
+
+static int translucent_compare(Operation *op, SlapReply *rs) {
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ translucent_info *ov = on->on_bi.bi_private;
+ AttributeAssertion *ava = op->orc_ava;
+ Entry *e = NULL;
+ BackendDB *db;
+ int rc;
+
+ Debug(LDAP_DEBUG_TRACE, "==> translucent_compare: <%s> %s:%s\n",
+ op->o_req_dn.bv_val, ava->aa_desc->ad_cname.bv_val, ava->aa_value.bv_val);
+
+/*
+** if the local backend has an entry for this attribute:
+** CONTINUE and let it do the compare;
+**
+*/
+ rc = overlay_entry_get_ov(op, &op->o_req_ndn, NULL, ava->aa_desc, 0, &e, on);
+ if(rc == LDAP_SUCCESS && e) {
+ overlay_entry_release_ov(op, e, 0, on);
+ return(SLAP_CB_CONTINUE);
+ }
+
+ if(ov->defer_db_open) {
+ send_ldap_error(op, rs, LDAP_UNAVAILABLE,
+ "remote DB not available");
+ return(rs->sr_err);
+ }
+/*
+** call compare() in the captive backend;
+** return the result;
+**
+*/
+ db = op->o_bd;
+ op->o_bd = &ov->db;
+ ov->db.be_acl = op->o_bd->be_acl;
+ rc = ov->db.bd_info->bi_op_compare(op, rs);
+ op->o_bd = db;
+
+ return(rc);
+}
+
+static int translucent_pwmod(Operation *op, SlapReply *rs) {
+ SlapReply nrs = { REP_RESULT };
+ Operation nop;
+
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ translucent_info *ov = on->on_bi.bi_private;
+ const struct berval bv_exop_pwmod = BER_BVC(LDAP_EXOP_MODIFY_PASSWD);
+ Entry *e = NULL, *re = NULL;
+ BackendDB *db;
+ int rc = 0;
+ slap_callback cb = { 0 };
+
+ if (!ov->pwmod_local) {
+ rs->sr_err = LDAP_CONSTRAINT_VIOLATION,
+ rs->sr_text = "attempt to modify password in local database";
+ return rs->sr_err;
+ }
+
+/*
+** fetch entry from the captive backend;
+** if it did not exist, fail;
+** release it, if captive backend supports this;
+**
+*/
+ db = op->o_bd;
+ op->o_bd = &ov->db;
+ ov->db.be_acl = op->o_bd->be_acl;
+ rc = ov->db.bd_info->bi_entry_get_rw(op, &op->o_req_ndn, NULL, NULL, 0, &re);
+ if(rc != LDAP_SUCCESS || re == NULL ) {
+ send_ldap_error((op), rs, LDAP_NO_SUCH_OBJECT,
+ "attempt to modify nonexistent local record");
+ return(rs->sr_err);
+ }
+ op->o_bd = db;
+/*
+** fetch entry from local backend;
+** if it exists:
+** return CONTINUE;
+*/
+
+ op->o_bd->bd_info = (BackendInfo *) on->on_info;
+ rc = be_entry_get_rw(op, &op->o_req_ndn, NULL, NULL, 0, &e);
+ op->o_bd->bd_info = (BackendInfo *) on;
+
+ if(e && rc == LDAP_SUCCESS) {
+ if(re) {
+ if(ov->db.bd_info->bi_entry_release_rw) {
+ op->o_bd = &ov->db;
+ ov->db.bd_info->bi_entry_release_rw(op, re, 0);
+ op->o_bd = db;
+ } else {
+ entry_free(re);
+ }
+ }
+ op->o_bd->bd_info = (BackendInfo *) on->on_info;
+ be_entry_release_r(op, e);
+ op->o_bd->bd_info = (BackendInfo *) on;
+ return SLAP_CB_CONTINUE;
+ }
+
+ /* don't leak remote entry copy */
+ if(re) {
+ if(ov->db.bd_info->bi_entry_release_rw) {
+ op->o_bd = &ov->db;
+ ov->db.bd_info->bi_entry_release_rw(op, re, 0);
+ op->o_bd = db;
+ } else {
+ entry_free(re);
+ }
+ }
+/*
+** glue_parent() for this Entry;
+** call bi_op_add() in local backend;
+**
+*/
+ e = entry_alloc();
+ ber_dupbv( &e->e_name, &op->o_req_dn );
+ ber_dupbv( &e->e_nname, &op->o_req_ndn );
+ e->e_attrs = NULL;
+
+ nop = *op;
+ nop.o_tag = LDAP_REQ_ADD;
+ cb.sc_response = slap_null_cb;
+ nop.oq_add.rs_e = e;
+
+ glue_parent(&nop);
+
+ nop.o_callback = &cb;
+ rc = on->on_info->oi_orig->bi_op_add(&nop, &nrs);
+ if ( nop.ora_e == e ) {
+ entry_free( e );
+ }
+
+ if ( rc == LDAP_SUCCESS ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ return rc;
+}
+
+static int translucent_exop(Operation *op, SlapReply *rs) {
+ SlapReply nrs = { REP_RESULT };
+
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ translucent_info *ov = on->on_bi.bi_private;
+ const struct berval bv_exop_pwmod = BER_BVC(LDAP_EXOP_MODIFY_PASSWD);
+
+ Debug(LDAP_DEBUG_TRACE, "==> translucent_exop: %s\n",
+ op->o_req_dn.bv_val, 0, 0);
+
+ if(ov->defer_db_open) {
+ send_ldap_error(op, rs, LDAP_UNAVAILABLE,
+ "remote DB not available");
+ return(rs->sr_err);
+ }
+
+ if ( bvmatch( &bv_exop_pwmod, &op->ore_reqoid ) ) {
+ return translucent_pwmod( op, rs );
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+/*
+** translucent_search_cb()
+** merge local data with remote data
+**
+** Four cases:
+** 1: remote search, no local filter
+** merge data and send immediately
+** 2: remote search, with local filter
+** merge data and save
+** 3: local search, no remote filter
+** merge data and send immediately
+** 4: local search, with remote filter
+** check list, merge, send, delete
+*/
+
+#define RMT_SIDE 0
+#define LCL_SIDE 1
+#define USE_LIST 2
+
+typedef struct trans_ctx {
+ BackendDB *db;
+ slap_overinst *on;
+ Filter *orig;
+ Avlnode *list;
+ int step;
+ int slimit;
+ AttributeName *attrs;
+} trans_ctx;
+
+static int translucent_search_cb(Operation *op, SlapReply *rs) {
+ trans_ctx *tc;
+ BackendDB *db;
+ slap_overinst *on;
+ translucent_info *ov;
+ Entry *le, *re;
+ Attribute *a, *ax, *an, *as = NULL;
+ int rc;
+ int test_f = 0;
+
+ tc = op->o_callback->sc_private;
+
+ /* Don't let the op complete while we're gathering data */
+ if ( rs->sr_type == REP_RESULT && ( tc->step & USE_LIST ))
+ return 0;
+
+ if(!op || !rs || rs->sr_type != REP_SEARCH || !rs->sr_entry)
+ return(SLAP_CB_CONTINUE);
+
+ Debug(LDAP_DEBUG_TRACE, "==> translucent_search_cb: %s\n",
+ rs->sr_entry->e_name.bv_val, 0, 0);
+
+ op->ors_slimit = tc->slimit + ( tc->slimit > 0 ? 1 : 0 );
+ if ( op->ors_attrs == slap_anlist_all_attributes ) {
+ op->ors_attrs = tc->attrs;
+ rs->sr_attrs = tc->attrs;
+ rs->sr_attr_flags = slap_attr_flags( rs->sr_attrs );
+ }
+
+ on = tc->on;
+ ov = on->on_bi.bi_private;
+
+ db = op->o_bd;
+ re = NULL;
+
+ /* If we have local, get remote */
+ if ( tc->step & LCL_SIDE ) {
+ le = rs->sr_entry;
+ /* If entry is already on list, use it */
+ if ( tc->step & USE_LIST ) {
+ re = tavl_delete( &tc->list, le, entry_dn_cmp );
+ if ( re ) {
+ if ( rs->sr_flags & REP_ENTRY_MUSTRELEASE ) {
+ rs->sr_flags ^= REP_ENTRY_MUSTRELEASE;
+ overlay_entry_release_ov( op, rs->sr_entry, 0, on );
+ }
+ if ( rs->sr_flags & REP_ENTRY_MUSTBEFREED ) {
+ rs->sr_flags ^= REP_ENTRY_MUSTBEFREED;
+ entry_free( rs->sr_entry );
+ }
+ rc = test_filter( op, re, tc->orig );
+ if ( rc == LDAP_COMPARE_TRUE ) {
+ rs->sr_flags |= REP_ENTRY_MUSTBEFREED;
+ rs->sr_entry = re;
+
+ if ( tc->slimit >= 0 && rs->sr_nentries >= tc->slimit ) {
+ return LDAP_SIZELIMIT_EXCEEDED;
+ }
+
+ return SLAP_CB_CONTINUE;
+ } else {
+ entry_free( re );
+ rs->sr_entry = NULL;
+ return 0;
+ }
+ }
+ }
+ op->o_bd = &ov->db;
+ rc = be_entry_get_rw( op, &rs->sr_entry->e_nname, NULL, NULL, 0, &re );
+ if ( rc == LDAP_SUCCESS && re ) {
+ Entry *tmp = entry_dup( re );
+ be_entry_release_r( op, re );
+ re = tmp;
+ test_f = 1;
+ }
+ } else {
+ /* Else we have remote, get local */
+ op->o_bd = tc->db;
+ le = NULL;
+ rc = overlay_entry_get_ov(op, &rs->sr_entry->e_nname, NULL, NULL, 0, &le, on);
+ if ( rc == LDAP_SUCCESS && le ) {
+ re = entry_dup( rs->sr_entry );
+ if ( rs->sr_flags & REP_ENTRY_MUSTRELEASE ) {
+ rs->sr_flags ^= REP_ENTRY_MUSTRELEASE;
+ overlay_entry_release_ov( op, rs->sr_entry, 0, on );
+ }
+ if ( rs->sr_flags & REP_ENTRY_MUSTBEFREED ) {
+ rs->sr_flags ^= REP_ENTRY_MUSTBEFREED;
+ entry_free( rs->sr_entry );
+ }
+ } else {
+ le = NULL;
+ }
+ }
+
+/*
+** if we got remote and local entry:
+** foreach local attr:
+** foreach remote attr:
+** if match, remote attr with local attr;
+** if new local, add to list;
+** append new local attrs to remote;
+**
+*/
+
+ if ( re && le ) {
+ for(ax = le->e_attrs; ax; ax = ax->a_next) {
+ for(a = re->e_attrs; a; a = a->a_next) {
+ if(a->a_desc == ax->a_desc) {
+ test_f = 1;
+ if(a->a_vals != a->a_nvals)
+ ber_bvarray_free(a->a_nvals);
+ ber_bvarray_free(a->a_vals);
+ ber_bvarray_dup_x( &a->a_vals, ax->a_vals, NULL );
+ if ( ax->a_vals == ax->a_nvals ) {
+ a->a_nvals = a->a_vals;
+ } else {
+ ber_bvarray_dup_x( &a->a_nvals, ax->a_nvals, NULL );
+ }
+ break;
+ }
+ }
+ if(a) continue;
+ an = attr_dup(ax);
+ an->a_next = as;
+ as = an;
+ }
+ /* Dispose of local entry */
+ if ( tc->step & LCL_SIDE ) {
+ if ( rs->sr_flags & REP_ENTRY_MUSTRELEASE ) {
+ rs->sr_flags ^= REP_ENTRY_MUSTRELEASE;
+ overlay_entry_release_ov( op, rs->sr_entry, 0, on );
+ }
+ if ( rs->sr_flags & REP_ENTRY_MUSTBEFREED ) {
+ rs->sr_flags ^= REP_ENTRY_MUSTBEFREED;
+ entry_free( rs->sr_entry );
+ }
+ } else {
+ overlay_entry_release_ov(op, le, 0, on);
+ }
+
+ /* literally append, so locals are always last */
+ if(as) {
+ if(re->e_attrs) {
+ for(ax = re->e_attrs; ax->a_next; ax = ax->a_next);
+ ax->a_next = as;
+ } else {
+ re->e_attrs = as;
+ }
+ }
+ /* If both filters, save entry for later */
+ if ( tc->step == (USE_LIST|RMT_SIDE) ) {
+ tavl_insert( &tc->list, re, entry_dn_cmp, avl_dup_error );
+ rs->sr_entry = NULL;
+ rc = 0;
+ } else {
+ /* send it now */
+ rs->sr_entry = re;
+ rs->sr_flags |= REP_ENTRY_MUSTBEFREED;
+ if ( test_f ) {
+ rc = test_filter( op, rs->sr_entry, tc->orig );
+ if ( rc == LDAP_COMPARE_TRUE ) {
+ rc = SLAP_CB_CONTINUE;
+ } else {
+ rc = 0;
+ }
+ } else {
+ rc = SLAP_CB_CONTINUE;
+ }
+ }
+ } else if ( le ) {
+ /* Only a local entry: remote was deleted
+ * Ought to delete the local too...
+ */
+ rc = 0;
+ } else if ( tc->step & USE_LIST ) {
+ /* Only a remote entry, but both filters:
+ * Test the complete filter
+ */
+ rc = test_filter( op, rs->sr_entry, tc->orig );
+ if ( rc == LDAP_COMPARE_TRUE ) {
+ rc = SLAP_CB_CONTINUE;
+ } else {
+ rc = 0;
+ }
+ } else {
+ /* Only a remote entry, only remote filter:
+ * just pass thru
+ */
+ rc = SLAP_CB_CONTINUE;
+ }
+
+ op->o_bd = db;
+
+ if ( rc == SLAP_CB_CONTINUE && tc->slimit >= 0 && rs->sr_nentries >= tc->slimit ) {
+ return LDAP_SIZELIMIT_EXCEEDED;
+ }
+
+ return rc;
+}
+
+/* Dup the filter, excluding invalid elements */
+static Filter *
+trans_filter_dup(Operation *op, Filter *f, AttributeName *an)
+{
+ Filter *n = NULL;
+
+ if ( !f )
+ return NULL;
+
+ switch( f->f_choice & SLAPD_FILTER_MASK ) {
+ case SLAPD_FILTER_COMPUTED:
+ n = op->o_tmpalloc( sizeof(Filter), op->o_tmpmemctx );
+ n->f_choice = f->f_choice;
+ n->f_result = f->f_result;
+ n->f_next = NULL;
+ break;
+
+ case LDAP_FILTER_PRESENT:
+ if ( ad_inlist( f->f_desc, an )) {
+ n = op->o_tmpalloc( sizeof(Filter), op->o_tmpmemctx );
+ n->f_choice = f->f_choice;
+ n->f_desc = f->f_desc;
+ n->f_next = NULL;
+ }
+ break;
+
+ case LDAP_FILTER_EQUALITY:
+ case LDAP_FILTER_GE:
+ case LDAP_FILTER_LE:
+ case LDAP_FILTER_APPROX:
+ case LDAP_FILTER_SUBSTRINGS:
+ case LDAP_FILTER_EXT:
+ if ( !f->f_av_desc || ad_inlist( f->f_av_desc, an )) {
+ n = op->o_tmpalloc( sizeof(Filter), op->o_tmpmemctx );
+ n->f_choice = f->f_choice;
+ n->f_ava = f->f_ava;
+ n->f_next = NULL;
+ }
+ break;
+
+ case LDAP_FILTER_AND:
+ case LDAP_FILTER_OR:
+ case LDAP_FILTER_NOT: {
+ Filter **p;
+
+ n = op->o_tmpalloc( sizeof(Filter), op->o_tmpmemctx );
+ n->f_choice = f->f_choice;
+ n->f_next = NULL;
+
+ for ( p = &n->f_list, f = f->f_list; f; f = f->f_next ) {
+ *p = trans_filter_dup( op, f, an );
+ if ( !*p )
+ continue;
+ p = &(*p)->f_next;
+ }
+ /* nothing valid in this list */
+ if ( !n->f_list ) {
+ op->o_tmpfree( n, op->o_tmpmemctx );
+ return NULL;
+ }
+ /* Only 1 element in this list */
+ if ((n->f_choice & SLAPD_FILTER_MASK) != LDAP_FILTER_NOT &&
+ !n->f_list->f_next ) {
+ f = n->f_list;
+ *n = *f;
+ op->o_tmpfree( f, op->o_tmpmemctx );
+ }
+ break;
+ }
+ }
+ return n;
+}
+
+static void
+trans_filter_free( Operation *op, Filter *f )
+{
+ Filter *n, *p, *next;
+
+ f->f_choice &= SLAPD_FILTER_MASK;
+
+ switch( f->f_choice ) {
+ case LDAP_FILTER_AND:
+ case LDAP_FILTER_OR:
+ case LDAP_FILTER_NOT:
+ /* Free in reverse order */
+ n = NULL;
+ for ( p = f->f_list; p; p = next ) {
+ next = p->f_next;
+ p->f_next = n;
+ n = p;
+ }
+ for ( p = n; p; p = next ) {
+ next = p->f_next;
+ trans_filter_free( op, p );
+ }
+ break;
+ default:
+ break;
+ }
+ op->o_tmpfree( f, op->o_tmpmemctx );
+}
+
+/*
+** translucent_search()
+** search via captive backend;
+** override results with any local data;
+**
+*/
+
+static int translucent_search(Operation *op, SlapReply *rs) {
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ translucent_info *ov = on->on_bi.bi_private;
+ slap_callback cb = { NULL, NULL, NULL, NULL };
+ trans_ctx tc;
+ Filter *fl, *fr;
+ struct berval fbv;
+ int rc = 0;
+
+ Debug(LDAP_DEBUG_TRACE, "==> translucent_search: <%s> %s\n",
+ op->o_req_dn.bv_val, op->ors_filterstr.bv_val, 0);
+
+ if(ov->defer_db_open) {
+ send_ldap_error(op, rs, LDAP_UNAVAILABLE,
+ "remote DB not available");
+ return(rs->sr_err);
+ }
+
+ fr = ov->remote ? trans_filter_dup( op, op->ors_filter, ov->remote ) : NULL;
+ fl = ov->local ? trans_filter_dup( op, op->ors_filter, ov->local ) : NULL;
+ cb.sc_response = (slap_response *) translucent_search_cb;
+ cb.sc_private = &tc;
+ cb.sc_next = op->o_callback;
+
+ ov->db.be_acl = op->o_bd->be_acl;
+ tc.db = op->o_bd;
+ tc.on = on;
+ tc.orig = op->ors_filter;
+ tc.list = NULL;
+ tc.step = 0;
+ tc.slimit = op->ors_slimit;
+ tc.attrs = NULL;
+ fbv = op->ors_filterstr;
+
+ op->o_callback = &cb;
+
+ if ( fr || !fl ) {
+ tc.attrs = op->ors_attrs;
+ op->ors_slimit = SLAP_NO_LIMIT;
+ op->ors_attrs = slap_anlist_all_attributes;
+ op->o_bd = &ov->db;
+ tc.step |= RMT_SIDE;
+ if ( fl ) {
+ tc.step |= USE_LIST;
+ op->ors_filter = fr;
+ filter2bv_x( op, fr, &op->ors_filterstr );
+ }
+ rc = ov->db.bd_info->bi_op_search(op, rs);
+ op->ors_attrs = tc.attrs;
+ op->o_bd = tc.db;
+ if ( fl ) {
+ op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
+ }
+ }
+ if ( fl && !rc ) {
+ tc.step |= LCL_SIDE;
+ op->ors_filter = fl;
+ filter2bv_x( op, fl, &op->ors_filterstr );
+ rc = overlay_op_walk( op, rs, op_search, on->on_info, on->on_next );
+ op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
+ }
+ op->ors_filterstr = fbv;
+ op->ors_filter = tc.orig;
+ op->o_callback = cb.sc_next;
+ rs->sr_attrs = op->ors_attrs;
+ rs->sr_attr_flags = slap_attr_flags( rs->sr_attrs );
+
+ /* Send out anything remaining on the list and finish */
+ if ( tc.step & USE_LIST ) {
+ if ( tc.list ) {
+ Avlnode *av;
+
+ av = tavl_end( tc.list, TAVL_DIR_LEFT );
+ while ( av ) {
+ rs->sr_entry = av->avl_data;
+ rc = test_filter( op, rs->sr_entry, op->ors_filter );
+ if ( rc == LDAP_COMPARE_TRUE ) {
+ rs->sr_flags = REP_ENTRY_MUSTBEFREED;
+ rc = send_search_entry( op, rs );
+ if ( rc ) break;
+ } else {
+ entry_free( rs->sr_entry );
+ }
+ av = tavl_next( av, TAVL_DIR_RIGHT );
+ }
+ tavl_free( tc.list, NULL );
+ rs->sr_entry = NULL;
+ }
+ send_ldap_result( op, rs );
+ }
+
+ op->ors_slimit = tc.slimit;
+
+ /* Free in reverse order */
+ if ( fl )
+ trans_filter_free( op, fl );
+ if ( fr )
+ trans_filter_free( op, fr );
+
+ return rc;
+}
+
+
+/*
+** translucent_bind()
+** pass bind request to captive backend;
+**
+*/
+
+static int translucent_bind(Operation *op, SlapReply *rs) {
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ translucent_info *ov = on->on_bi.bi_private;
+ BackendDB *db;
+ slap_callback sc = { 0 }, *save_cb;
+ int rc;
+
+ Debug(LDAP_DEBUG_TRACE, "translucent_bind: <%s> method %d\n",
+ op->o_req_dn.bv_val, op->orb_method, 0);
+
+ if(ov->defer_db_open) {
+ send_ldap_error(op, rs, LDAP_UNAVAILABLE,
+ "remote DB not available");
+ return(rs->sr_err);
+ }
+
+ if (ov->bind_local) {
+ sc.sc_response = slap_null_cb;
+ save_cb = op->o_callback;
+ op->o_callback = &sc;
+ }
+
+ db = op->o_bd;
+ op->o_bd = &ov->db;
+ ov->db.be_acl = op->o_bd->be_acl;
+ rc = ov->db.bd_info->bi_op_bind(op, rs);
+ op->o_bd = db;
+
+ if (ov->bind_local) {
+ op->o_callback = save_cb;
+ if (rc != LDAP_SUCCESS) {
+ rc = SLAP_CB_CONTINUE;
+ }
+ }
+
+ return rc;
+}
+
+/*
+** translucent_connection_destroy()
+** pass disconnect notification to captive backend;
+**
+*/
+
+static int translucent_connection_destroy(BackendDB *be, Connection *conn) {
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+ translucent_info *ov = on->on_bi.bi_private;
+ int rc = 0;
+
+ Debug(LDAP_DEBUG_TRACE, "translucent_connection_destroy\n", 0, 0, 0);
+
+ rc = ov->db.bd_info->bi_connection_destroy(&ov->db, conn);
+
+ return(rc);
+}
+
+/*
+** translucent_db_config()
+** pass config directives to captive backend;
+** parse unrecognized directives ourselves;
+**
+*/
+
+static int translucent_db_config(
+ BackendDB *be,
+ const char *fname,
+ int lineno,
+ int argc,
+ char **argv
+)
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+ translucent_info *ov = on->on_bi.bi_private;
+
+ Debug(LDAP_DEBUG_TRACE, "==> translucent_db_config: %s\n",
+ argc ? argv[0] : "", 0, 0);
+
+ /* Something for the captive database? */
+ if ( ov->db.bd_info && ov->db.bd_info->bi_db_config )
+ return ov->db.bd_info->bi_db_config( &ov->db, fname, lineno,
+ argc, argv );
+ return SLAP_CONF_UNKNOWN;
+}
+
+/*
+** translucent_db_init()
+** initialize the captive backend;
+**
+*/
+
+static int translucent_db_init(BackendDB *be, ConfigReply *cr) {
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+ translucent_info *ov;
+
+ Debug(LDAP_DEBUG_TRACE, "==> translucent_db_init\n", 0, 0, 0);
+
+ ov = ch_calloc(1, sizeof(translucent_info));
+ on->on_bi.bi_private = ov;
+ ov->db = *be;
+ ov->db.be_private = NULL;
+ ov->defer_db_open = 1;
+
+ if ( !backend_db_init( "ldap", &ov->db, -1, NULL )) {
+ Debug( LDAP_DEBUG_CONFIG, "translucent: unable to open captive back-ldap\n", 0, 0, 0);
+ return 1;
+ }
+ SLAP_DBFLAGS(be) |= SLAP_DBFLAG_NO_SCHEMA_CHECK;
+ SLAP_DBFLAGS(be) |= SLAP_DBFLAG_NOLASTMOD;
+
+ return 0;
+}
+
+/*
+** translucent_db_open()
+** if the captive backend has an open() method, call it;
+**
+*/
+
+static int translucent_db_open(BackendDB *be, ConfigReply *cr) {
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+ translucent_info *ov = on->on_bi.bi_private;
+ int rc;
+
+ Debug(LDAP_DEBUG_TRACE, "==> translucent_db_open\n", 0, 0, 0);
+
+ /* need to inherit something from the original database... */
+ ov->db.be_def_limit = be->be_def_limit;
+ ov->db.be_limits = be->be_limits;
+ ov->db.be_acl = be->be_acl;
+ ov->db.be_dfltaccess = be->be_dfltaccess;
+
+ if ( ov->defer_db_open )
+ return 0;
+
+ rc = backend_startup_one( &ov->db, cr );
+
+ if(rc) Debug(LDAP_DEBUG_TRACE,
+ "translucent: bi_db_open() returned error %d\n", rc, 0, 0);
+
+ return(rc);
+}
+
+/*
+** translucent_db_close()
+** if the captive backend has a close() method, call it
+**
+*/
+
+static int
+translucent_db_close( BackendDB *be, ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+ translucent_info *ov = on->on_bi.bi_private;
+ int rc = 0;
+
+ Debug(LDAP_DEBUG_TRACE, "==> translucent_db_close\n", 0, 0, 0);
+
+ if ( ov && ov->db.bd_info && ov->db.bd_info->bi_db_close ) {
+ rc = ov->db.bd_info->bi_db_close(&ov->db, NULL);
+ }
+
+ return(rc);
+}
+
+/*
+** translucent_db_destroy()
+** if the captive backend has a db_destroy() method, call it;
+** free any config data
+**
+*/
+
+static int
+translucent_db_destroy( BackendDB *be, ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+ translucent_info *ov = on->on_bi.bi_private;
+ int rc = 0;
+
+ Debug(LDAP_DEBUG_TRACE, "==> translucent_db_destroy\n", 0, 0, 0);
+
+ if ( ov ) {
+ if ( ov->remote )
+ anlist_free( ov->remote, 1, NULL );
+ if ( ov->local )
+ anlist_free( ov->local, 1, NULL );
+ if ( ov->db.be_private != NULL ) {
+ backend_stopdown_one( &ov->db );
+ }
+
+ ch_free(ov);
+ on->on_bi.bi_private = NULL;
+ }
+
+ return(rc);
+}
+
+/*
+** translucent_initialize()
+** initialize the slap_overinst with our entry points;
+**
+*/
+
+int translucent_initialize() {
+
+ int rc;
+
+ Debug(LDAP_DEBUG_TRACE, "==> translucent_initialize\n", 0, 0, 0);
+
+ translucent.on_bi.bi_type = "translucent";
+ translucent.on_bi.bi_db_init = translucent_db_init;
+ translucent.on_bi.bi_db_config = translucent_db_config;
+ translucent.on_bi.bi_db_open = translucent_db_open;
+ translucent.on_bi.bi_db_close = translucent_db_close;
+ translucent.on_bi.bi_db_destroy = translucent_db_destroy;
+ translucent.on_bi.bi_op_bind = translucent_bind;
+ translucent.on_bi.bi_op_add = translucent_add;
+ translucent.on_bi.bi_op_modify = translucent_modify;
+ translucent.on_bi.bi_op_modrdn = translucent_modrdn;
+ translucent.on_bi.bi_op_delete = translucent_delete;
+ translucent.on_bi.bi_op_search = translucent_search;
+ translucent.on_bi.bi_op_compare = translucent_compare;
+ translucent.on_bi.bi_connection_destroy = translucent_connection_destroy;
+ translucent.on_bi.bi_extended = translucent_exop;
+
+ translucent.on_bi.bi_cf_ocs = translucentocs;
+ rc = config_register_schema ( translucentcfg, translucentocs );
+ if ( rc ) return rc;
+
+ return(overlay_register(&translucent));
+}
+
+#if SLAPD_OVER_TRANSLUCENT == SLAPD_MOD_DYNAMIC && defined(PIC)
+int init_module(int argc, char *argv[]) {
+ return translucent_initialize();
+}
+#endif
+
+#endif /* SLAPD_OVER_TRANSLUCENT */