]> git.sur5r.net Git - openldap/blobdiff - servers/slapd/overlays/translucent.c
ITS#6103: rev 1.331 followup for LDAP_CONNECTIONLESS
[openldap] / servers / slapd / overlays / translucent.c
index 9d88129f40660f56e6bb0bee61be841e832d9458..92d9a211517b2f9d65ac73a18a5624b37edee06c 100644 (file)
@@ -2,7 +2,7 @@
 /* $OpenLDAP$ */
 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
  *
- * Copyright 2004-2007 The OpenLDAP Foundation.
+ * Copyright 2004-2009 The OpenLDAP Foundation.
  * Portions Copyright 2005 Symas Corporation.
  * All rights reserved.
  *
@@ -41,6 +41,8 @@ typedef struct translucent_info {
        int strict;
        int no_glue;
        int defer_db_open;
+       int bind_local;
+       int pwmod_local;
 } translucent_info;
 
 static ConfigLDAPadd translucent_ldadd;
@@ -67,25 +69,29 @@ static ConfigTable translucentcfg[] = {
          "DESC 'Disable automatic glue records for ADD and MODRDN' "
          "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
        { "translucent_local", "attr[,attr...]", 1, 2, 0,
-         ARG_STRING|ARG_MAGIC|TRANS_LOCAL,
+         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_STRING|ARG_MAGIC|TRANS_REMOTE,
+         ARG_MAGIC|TRANS_REMOTE,
          translucent_cf_gen,
          "( OLcfgOvAt:14.4 NAME 'olcTranslucentRemote' "
          "DESC 'Attributes to use in remote search filter' "
          "SYNTAX OMsDirectoryString )", NULL, NULL },
-       { NULL, NULL, 0, 0, 0, ARG_IGNORED }
-};
-
-static ConfigTable transdummy[] = {
-       { "", "", 0, 0, 0, ARG_IGNORED,
-               NULL, "( OLcfgGlAt:13 NAME 'olcDatabase' "
-                       "DESC 'The backend type for a database instance' "
-                       "SUP olcBackend SINGLE-VALUE X-ORDERED 'SIBLINGS' )", 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 }
 };
 
@@ -94,12 +100,14 @@ static ConfigOCs translucentocs[] = {
          "NAME 'olcTranslucentConfig' "
          "DESC 'Translucent configuration' "
          "SUP olcOverlayConfig "
-         "MAY ( olcTranslucentStrict $ olcTranslucentNoGlue ) )",
+         "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, transdummy, translucent_ldadd },
+         "AUXILIARY )", Cft_Misc, olcDatabaseDummy, translucent_ldadd },
        { NULL, 0, NULL }
 };
 /* for translucent_init() */
@@ -107,7 +115,7 @@ static ConfigOCs translucentocs[] = {
 static int
 translucent_ldadd_cleanup( ConfigArgs *ca )
 {
-       slap_overinst *on = ca->private;
+       slap_overinst *on = ca->ca_private;
        translucent_info *ov = on->on_bi.bi_private;
 
        ov->defer_db_open = 0;
@@ -129,7 +137,7 @@ translucent_ldadd( CfEntryInfo *cei, Entry *e, ConfigArgs *ca )
        on = (slap_overinst *)cei->ce_bi;
        ov = on->on_bi.bi_private;
        ca->be = &ov->db;
-       ca->private = on;
+       ca->ca_private = on;
        if ( CONFIG_ONLINE_ADD( ca ))
                ca->cleanup = translucent_ldadd_cleanup;
        else
@@ -151,7 +159,7 @@ translucent_cfadd( Operation *op, SlapReply *rs, Entry *e, ConfigArgs *ca )
        /* FIXME: should not hardcode "olcDatabase" here */
        bv.bv_len = snprintf( ca->cr_msg, sizeof( ca->cr_msg ),
                "olcDatabase=%s", ov->db.bd_info->bi_type );
-       if ( bv.bv_len < 0 || bv.bv_len >= sizeof( ca->cr_msg ) ) {
+       if ( bv.bv_len >= sizeof( ca->cr_msg ) ) {
                return -1;
        }
        bv.bv_val = ca->cr_msg;
@@ -182,6 +190,8 @@ translucent_cf_gen( ConfigArgs *c )
                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 );
                }
@@ -422,6 +432,7 @@ static int translucent_modify(Operation *op, SlapReply *rs) {
 
        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,
@@ -602,7 +613,7 @@ static int translucent_compare(Operation *op, SlapReply *rs) {
 **
 */
        rc = overlay_entry_get_ov(op, &op->o_req_ndn, NULL, ava->aa_desc, 0, &e, on);
-       if(e && rc == LDAP_SUCCESS) {
+       if(rc == LDAP_SUCCESS && e) {
                overlay_entry_release_ov(op, e, 0, on);
                return(SLAP_CB_CONTINUE);
        }
@@ -619,12 +630,136 @@ static int translucent_compare(Operation *op, SlapReply *rs) {
 */
        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
@@ -648,8 +783,6 @@ typedef struct trans_ctx {
        BackendDB *db;
        slap_overinst *on;
        Filter *orig;
-       Filter *rmt;
-       Filter *lcl;
        Avlnode *list;
        int step;
 } trans_ctx;
@@ -662,6 +795,7 @@ static int translucent_search_cb(Operation *op, SlapReply *rs) {
        Entry *le, *re;
        Attribute *a, *ax, *an, *as = NULL;
        int rc;
+       int test_f = 0;
 
        tc = op->o_callback->sc_private;
 
@@ -690,7 +824,7 @@ static int translucent_search_cb(Operation *op, SlapReply *rs) {
                        if ( re ) {
                                if ( rs->sr_flags & REP_ENTRY_MUSTRELEASE ) {
                                        rs->sr_flags ^= REP_ENTRY_MUSTRELEASE;
-                                       be_entry_release_r( op, rs->sr_entry );
+                                       overlay_entry_release_ov( op, rs->sr_entry, 0, on );
                                }
                                if ( rs->sr_flags & REP_ENTRY_MUSTBEFREED ) {
                                        rs->sr_flags ^= REP_ENTRY_MUSTBEFREED;
@@ -714,6 +848,7 @@ static int translucent_search_cb(Operation *op, SlapReply *rs) {
                        Entry *tmp = entry_dup( re );
                        be_entry_release_r( op, re );
                        re = tmp;
+                       test_f = 1;
                }
        } else {
        /* Else we have remote, get local */
@@ -723,7 +858,7 @@ static int translucent_search_cb(Operation *op, SlapReply *rs) {
                        re = entry_dup( rs->sr_entry );
                        if ( rs->sr_flags & REP_ENTRY_MUSTRELEASE ) {
                                rs->sr_flags ^= REP_ENTRY_MUSTRELEASE;
-                               be_entry_release_r( op, rs->sr_entry );
+                               overlay_entry_release_ov( op, rs->sr_entry, 0, on );
                        }
                        if ( rs->sr_flags & REP_ENTRY_MUSTBEFREED ) {
                                rs->sr_flags ^= REP_ENTRY_MUSTBEFREED;
@@ -766,7 +901,7 @@ static int translucent_search_cb(Operation *op, SlapReply *rs) {
                if ( tc->step & LCL_SIDE ) {
                        if ( rs->sr_flags & REP_ENTRY_MUSTRELEASE ) {
                                rs->sr_flags ^= REP_ENTRY_MUSTRELEASE;
-                               be_entry_release_r( op, rs->sr_entry );
+                               overlay_entry_release_ov( op, rs->sr_entry, 0, on );
                        }
                        if ( rs->sr_flags & REP_ENTRY_MUSTBEFREED ) {
                                rs->sr_flags ^= REP_ENTRY_MUSTBEFREED;
@@ -785,19 +920,25 @@ static int translucent_search_cb(Operation *op, SlapReply *rs) {
                                re->e_attrs = as;
                        }
                }
+               /* If both filters, save entry for later */
                if ( tc->step == (USE_LIST|RMT_SIDE) ) {
-                       rc = test_filter( op, re, tc->orig );
-                       if ( rc == LDAP_COMPARE_TRUE ) {
-                               tavl_insert( &tc->list, re, entry_dn_cmp, avl_dup_error );
-                       } else {
-                               entry_free( re );
-                       }
+                       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;
-                       rc = SLAP_CB_CONTINUE;
+                       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
@@ -886,7 +1027,7 @@ trans_filter_dup(Operation *op, Filter *f, AttributeName *an)
                        return NULL;
                }
                /* Only 1 element in this list */
-               if ((f->f_choice & SLAPD_FILTER_MASK) != LDAP_FILTER_NOT &&
+               if ((n->f_choice & SLAPD_FILTER_MASK) != LDAP_FILTER_NOT &&
                        !n->f_list->f_next ) {
                        f = n->f_list;
                        *n = *f;
@@ -898,6 +1039,35 @@ trans_filter_dup(Operation *op, Filter *f, AttributeName *an)
        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;
@@ -911,6 +1081,7 @@ static int translucent_search(Operation *op, SlapReply *rs) {
        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",
@@ -928,30 +1099,38 @@ static int translucent_search(Operation *op, SlapReply *rs) {
        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.rmt = fr;
-       tc.lcl = fl;
        tc.list = NULL;
        tc.step = 0;
+       fbv = op->ors_filterstr;
 
        op->o_callback = &cb;
 
        if ( fr || !fl ) {
-               tc.step |= RMT_SIDE;
-               if ( fl ) tc.step |= USE_LIST;
                op->o_bd = &ov->db;
-               if ( fl )
+               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->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;
        /* Send out anything remaining on the list and finish */
@@ -961,17 +1140,29 @@ static int translucent_search(Operation *op, SlapReply *rs) {
 
                        av = tavl_end( tc.list, TAVL_DIR_LEFT );
                        while ( av ) {
-                               rs->sr_flags = REP_ENTRY_MUSTBEFREED;
                                rs->sr_entry = av->avl_data;
-                               rc = send_search_entry( op, rs );
-                               if ( rc ) break;
+                               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 );
        }
 
+       /* Free in reverse order */
+       if ( fl )
+               trans_filter_free( op, fl );
+       if ( fr )
+               trans_filter_free( op, fr );
+
        return rc;
 }
 
@@ -986,6 +1177,7 @@ 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",
@@ -996,10 +1188,26 @@ static int translucent_bind(Operation *op, SlapReply *rs) {
                        "remote DB not available");
                return(rs->sr_err);
        }
+
+       if (ov->bind_local) {
+               sc.sc_response = slap_null_cb;
+               save_cb = op->o_callback;
+               op->o_callback = &sc;
+       }
+
        db = op->o_bd;
        op->o_bd = &ov->db;
+       ov->db.be_acl = op->o_bd->be_acl;
        rc = ov->db.bd_info->bi_op_bind(op, rs);
        op->o_bd = db;
+
+       if (ov->bind_local) {
+               op->o_callback = save_cb;
+               if (rc != LDAP_SUCCESS) {
+                       rc = SLAP_CB_CONTINUE;
+               }
+       }
+
        return rc;
 }
 
@@ -1065,7 +1273,6 @@ static int translucent_db_init(BackendDB *be, ConfigReply *cr) {
        on->on_bi.bi_private = ov;
        ov->db = *be;
        ov->db.be_private = NULL;
-       ov->db.be_pcl_mutexp = &ov->db.be_pcl_mutex;
        ov->defer_db_open = 1;
 
        if ( !backend_db_init( "ldap", &ov->db, -1, NULL )) {
@@ -1100,7 +1307,7 @@ static int translucent_db_open(BackendDB *be, ConfigReply *cr) {
        if ( ov->defer_db_open )
                return 0;
 
-       rc = backend_startup_one( &ov->db, NULL );
+       rc = backend_startup_one( &ov->db, cr );
 
        if(rc) Debug(LDAP_DEBUG_TRACE,
                "translucent: bi_db_open() returned error %d\n", rc, 0, 0);
@@ -1188,6 +1395,7 @@ int translucent_initialize() {
        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 );