diff options
-rw-r--r-- | rwm.c | 2711 | ||||
-rw-r--r-- | rwm.h | 187 | ||||
-rw-r--r-- | rwmconf.c | 417 | ||||
-rw-r--r-- | rwmdn.c | 215 | ||||
-rw-r--r-- | rwmmap.c | 1313 | ||||
-rw-r--r-- | translucent.c | 1434 |
6 files changed, 6277 insertions, 0 deletions
@@ -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 */ @@ -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 */ @@ -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 = ≻ + } + + 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 */ |