The branch, master has been updated
       via  850cca3 add a demo script for dirsync
       via  c2fa348 s4-dsdb: add unit tests for dirsync control
       via  fa400af s4-dsdb: implementation of the dirsync control
       via  7b4e1e7 s4-dsdb: introduce dsdb_module_search_tree
       via  37b1662 s4-dsdb: relax a bit the checks on read acl when dirsync 
control is specified
       via  1d0fc44 s4-dsdb: create flag for requesting ACL relax in case of 
DIRSYNC request
       via  df83e9c s4: do not change the critical flag when it's on a dirsync 
control
       via  fae229a selftest: Allow to test samba4 with ACL on read set
      from  49c99d0 s4: add blackbox test for rename

http://gitweb.samba.org/?p=samba.git;a=shortlog;h=master


- Log -----------------------------------------------------------------
commit 850cca3996605a45d3f203318a90d3941934a792
Author: Matthieu Patou <m...@matws.net>
Date:   Mon Feb 7 09:55:26 2011 +0300

    add a demo script for dirsync
    
    Signed-off-by: Andrew Tridgell <tri...@samba.org>
    
    Autobuild-User: Matthieu Patou <m...@samba.org>
    Autobuild-Date: Sat May 21 15:40:26 CEST 2011 on sn-devel-104

commit c2fa3488317fa6b5eac0c7acc1fbed1e8cebcb7d
Author: Matthieu Patou <m...@matws.net>
Date:   Sun Feb 27 12:24:45 2011 +0300

    s4-dsdb: add unit tests for dirsync control
    
    Signed-off-by: Andrew Tridgell <tri...@samba.org>

commit fa400af18b7fdba2edd6c3b4c335dab64481545a
Author: Matthieu Patou <m...@matws.net>
Date:   Thu Jan 13 21:55:11 2011 +0300

    s4-dsdb: implementation of the dirsync control
    
    Signed-off-by: Andrew Tridgell <tri...@samba.org>

commit 7b4e1e78bef7ece31ee01ef3a1d7cfc3810ab27d
Author: Matthieu Patou <m...@matws.net>
Date:   Sun Mar 20 01:20:22 2011 +0300

    s4-dsdb: introduce dsdb_module_search_tree
    
    With this function your own search tree can be specified
    
    This function is similar to ldb_build_search_req_ex as it allows to
    pass a parse tree structure.
    
    Signed-off-by: Andrew Tridgell <tri...@samba.org>

commit 37b1662a38259d59508faa1b6226406b02504a5b
Author: Matthieu Patou <m...@matws.net>
Date:   Tue Mar 8 01:02:32 2011 +0300

    s4-dsdb: relax a bit the checks on read acl when dirsync control is 
specified
    
    Signed-off-by: Andrew Tridgell <tri...@samba.org>

commit 1d0fc445fae4b908ac475d0beb5e1d8d14a3efcb
Author: Matthieu Patou <m...@matws.net>
Date:   Sat Apr 16 11:46:40 2011 +0400

    s4-dsdb: create flag for requesting ACL relax in case of DIRSYNC request
    
    Signed-off-by: Andrew Tridgell <tri...@samba.org>

commit df83e9c15e93502e77cfa877d6bdf1beb9b4048f
Author: Matthieu Patou <m...@matws.net>
Date:   Mon Feb 7 09:58:17 2011 +0300

    s4: do not change the critical flag when it's on a dirsync control
    
    Signed-off-by: Andrew Tridgell <tri...@samba.org>

commit fae229aa3df470cf9cd87b42d60aa6be4211d1ae
Author: Matthieu Patou <m...@matws.net>
Date:   Thu Apr 14 09:48:14 2011 +0400

    selftest: Allow to test samba4 with ACL on read set
    
    Signed-off-by: Andrew Tridgell <tri...@samba.org>

-----------------------------------------------------------------------

Summary of changes:
 selftest/target/Samba4.pm                    |    3 +
 source4/dsdb/samdb/ldb_modules/acl_read.c    |   54 +-
 source4/dsdb/samdb/ldb_modules/dirsync.c     | 1359 ++++++++++++++++++++++++++
 source4/dsdb/samdb/ldb_modules/rootdse.c     |    6 +-
 source4/dsdb/samdb/ldb_modules/samba_dsdb.c  |    1 +
 source4/dsdb/samdb/ldb_modules/util.c        |   87 ++-
 source4/dsdb/samdb/ldb_modules/wscript_build |    9 +
 source4/dsdb/samdb/samdb.h                   |    1 +
 source4/dsdb/tests/python/dirsync.py         |  713 ++++++++++++++
 source4/scripting/devel/demodirsync.py       |  156 +++
 source4/selftest/knownfail                   |    2 +
 source4/selftest/tests.py                    |    1 +
 12 files changed, 2355 insertions(+), 37 deletions(-)
 create mode 100644 source4/dsdb/samdb/ldb_modules/dirsync.c
 create mode 100755 source4/dsdb/tests/python/dirsync.py
 create mode 100755 source4/scripting/devel/demodirsync.py


Changeset truncated at 500 lines:

diff --git a/selftest/target/Samba4.pm b/selftest/target/Samba4.pm
index bbe64a9..d27ee41 100644
--- a/selftest/target/Samba4.pm
+++ b/selftest/target/Samba4.pm
@@ -578,8 +578,11 @@ sub provision_raw_step1($$)
                warn("can't open $ctx->{smb_conf}$?");
                return undef;
        }
+       my $acl = "false";
+       $acl = "true" if (defined $ENV{WITH_ACL});
        print CONFFILE "
 [global]
+       acl:search = $acl
        netbios name = $ctx->{netbiosname}
        posix:eadb = $ctx->{lockdir}/eadb.tdb
        workgroup = $ctx->{domain}
diff --git a/source4/dsdb/samdb/ldb_modules/acl_read.c 
b/source4/dsdb/samdb/ldb_modules/acl_read.c
index 181619a..35a840e 100644
--- a/source4/dsdb/samdb/ldb_modules/acl_read.c
+++ b/source4/dsdb/samdb/ldb_modules/acl_read.c
@@ -47,6 +47,7 @@ struct aclread_context {
        bool sd;
        bool instance_type;
        bool object_sid;
+       bool indirsync;
 };
 
 struct aclread_private {
@@ -158,18 +159,41 @@ static int aclread_callback(struct ldb_request *req, 
struct ldb_reply *ares)
                                                             access_mask,
                                                             attr);
 
-                        if (ret == LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS) {
-                                /* do not return this entry if attribute is
-                                   part of the search filter */
-                                if 
(dsdb_attr_in_parse_tree(ac->req->op.search.tree,
-                                                            
msg->elements[i].name)) {
-                                        talloc_free(tmp_ctx);
-                                        return LDB_SUCCESS;
-                                }
-                                aclread_mark_inaccesslible(&msg->elements[i]);
-                        } else if (ret != LDB_SUCCESS) {
-                                goto fail;
-                        }
+                       /*
+                        * Dirsync control needs the replpropertymetadata 
attribute
+                        * so return it as it will be removed by the control
+                        * in anycase.
+                        */
+                       if (ret == LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS) {
+                               if (!ac->indirsync) {
+                                       /* do not return this entry if 
attribute is
+                                       part of the search filter */
+                                       if 
(dsdb_attr_in_parse_tree(ac->req->op.search.tree,
+                                                               
msg->elements[i].name)) {
+                                               talloc_free(tmp_ctx);
+                                               return LDB_SUCCESS;
+                                       }
+                                       
aclread_mark_inaccesslible(&msg->elements[i]);
+                               } else {
+                                       /*
+                                        * We are doing dirysnc answers
+                                        * and the object shouldn't be returned 
(normally)
+                                        * but we will return it without 
replPropertyMetaData
+                                        * so that the dirysync module will do 
what is needed
+                                        * (remove the object if it is not 
deleted, or return
+                                        * just the objectGUID if it's deleted).
+                                        */
+                                       if 
(dsdb_attr_in_parse_tree(ac->req->op.search.tree,
+                                                               
msg->elements[i].name)) {
+                                               ldb_msg_remove_attr(msg, 
"replPropertyMetaData");
+                                               break;
+                                       } else {
+                                               
aclread_mark_inaccesslible(&msg->elements[i]);
+                                       }
+                               }
+                       } else if (ret != LDB_SUCCESS) {
+                               goto fail;
+                       }
                 }
                 for (i=0; i < msg->num_elements; i++) {
                         if (!aclread_is_inaccessible(&msg->elements[i])) {
@@ -224,6 +248,7 @@ static int aclread_search(struct ldb_module *module, struct 
ldb_request *req)
        struct aclread_context *ac;
        struct ldb_request *down_req;
        struct ldb_control *as_system = ldb_request_get_control(req, 
LDB_CONTROL_AS_SYSTEM_OID);
+       uint32_t flags = ldb_req_get_custom_flags(req);
        struct ldb_result *res;
        struct aclread_private *p;
        bool is_untrusted = ldb_req_is_untrusted(req);
@@ -284,6 +309,11 @@ static int aclread_search(struct ldb_module *module, 
struct ldb_request *req)
        ac->module = module;
        ac->req = req;
        ac->schema = dsdb_get_schema(ldb, req);
+       if (flags & DSDB_ACL_CHECKS_DIRSYNC_FLAG) {
+               ac->indirsync = true;
+       } else {
+               ac->indirsync = false;
+       }
        if (!ac->schema) {
                return ldb_operr(ldb);
        }
diff --git a/source4/dsdb/samdb/ldb_modules/dirsync.c 
b/source4/dsdb/samdb/ldb_modules/dirsync.c
new file mode 100644
index 0000000..64c5047
--- /dev/null
+++ b/source4/dsdb/samdb/ldb_modules/dirsync.c
@@ -0,0 +1,1359 @@
+/*
+   SAMDB control module
+
+   Copyright (C) Matthieu Patou <m...@matws.net> 2011
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+#include "includes.h"
+#include "ldb/include/ldb.h"
+#include "ldb/include/ldb_errors.h"
+#include "ldb/include/ldb_module.h"
+#include "libcli/security/security.h"
+#include "librpc/gen_ndr/drsblobs.h"
+#include "librpc/gen_ndr/ndr_drsblobs.h"
+#include "librpc/ndr/libndr.h"
+#include "dsdb/samdb/samdb.h"
+#include "util.h"
+
+#define LDAP_DIRSYNC_OBJECT_SECURITY           0x01
+#define LDAP_DIRSYNC_ANCESTORS_FIRST_ORDER     0x800
+#define LDAP_DIRSYNC_PUBLIC_DATA_ONLY          0x2000
+#define LDAP_DIRSYNC_INCREMENTAL_VALUES                0x80000000
+
+
+struct dirsync_context {
+       struct ldb_module *module;
+       struct ldb_request *req;
+
+       /*
+        * We keep a track of the number of attributes that we
+        * add just for the need of the implementation
+        * it will be usefull to track then entries that needs not to
+        * be returned because there is no real change
+        */
+
+       unsigned int nbDefaultAttrs;
+       uint64_t highestUSN;
+       uint64_t fromreqUSN;
+       uint32_t cursor_size;
+       bool noextended;
+       bool linkIncrVal;
+       bool localonly;
+       bool partial;
+       bool assystem;
+       int functional_level;
+       const struct GUID *our_invocation_id;
+       const struct dsdb_schema *schema;
+       struct ldb_dn *nc_root;
+       struct drsuapi_DsReplicaCursor *cursors;
+};
+
+
+static int dirsync_filter_entry(struct ldb_request *req,
+                                       struct ldb_message *msg,
+                                       struct ldb_control **controls,
+                                       struct dirsync_context *dsc,
+                                       bool referral)
+{
+       struct ldb_context *ldb;
+       uint64_t val;
+       enum ndr_err_code ndr_err;
+       uint32_t n;
+       int i;
+       unsigned int size, j;
+       uint32_t deletedattr;
+       struct ldb_val *replMetaData = NULL;
+       struct replPropertyMetaDataBlob rmd;
+       const struct dsdb_attribute *attr;
+       const char **listAttr = NULL;
+       bool namereturned = false;
+       bool nameasked = false;
+       NTSTATUS status;
+       /* Ajustment for the added attributes, it will reduce the number of
+        * expected to be here attributes*/
+       unsigned int delta = 0;
+       const char **myaccept = NULL;
+       const char *emptyaccept[] = { NULL };
+       const char *extendedaccept[] = { "GUID", "SID", "WKGUID", NULL };
+       const char *rdn = NULL;
+       struct ldb_message_element *el;
+       struct ldb_message *newmsg;
+       bool keep = false;
+       /*
+        * Where we asked to do extended dn ?
+        * if so filter out everything bug GUID, SID, WKGUID,
+        * if not filter out everything (just keep the dn).
+        */
+       if ( dsc->noextended == true ) {
+               myaccept = emptyaccept;
+       } else {
+               myaccept = extendedaccept;
+       }
+       ldb = ldb_module_get_ctx(dsc->module);
+
+       if (msg->num_elements == 0) {
+               /*
+                       * Entry that we don't really have access to
+                       */
+               return LDB_SUCCESS;
+       }
+       ldb_dn_extended_filter(msg->dn, myaccept);
+
+       /*
+       * If the RDN starts with CN then the CN attribute is never returned
+       */
+       rdn = ldb_dn_get_rdn_name(msg->dn);
+
+       deletedattr = 0;
+       /*
+        * if objectGUID is asked and we are dealing for the referrals entries 
and
+        * the usn searched is 0 then we didn't count the objectGUID as an 
automatically
+        * returned attribute, do to so we increament delta.
+        */
+       if (referral == true &&
+                       ldb_attr_in_list(req->op.search.attrs, "objectGUID") &&
+                       dsc->fromreqUSN == 0) {
+               delta++;
+       }
+
+
+       /*
+        * In terms of big O notation this is not the best algorithm,
+        * but we try our best not to make the worse one.
+        * We are obliged to run through the n message's elements
+        * and through the p elements of the replPropertyMetaData.
+        *
+        * It turns out that we are crawling twice the message's elements
+        * the first crawl is to remove the non replicated and generated
+        * attributes. The second one is to remove attributes that haven't
+        * a USN > as the requested one.
+        *
+        * In the second crawl we are reading the list of elements in the
+        * replPropertyMetaData for each remaining replicated attribute.
+        * In order to keep the list small
+        *
+        * We have a O(n'*p') complexity, in worse case n' = n and p' = p
+        * but in most case n' = n/2 (at least half of returned attributes
+        * are not replicated or generated) and p' is small as we
+        * list only the attribute that have been modified since last 
interogation
+        *
+        */
+       newmsg = talloc_zero(dsc->req, struct ldb_message);
+       if (newmsg == NULL) {
+               return ldb_oom(ldb);
+       }
+       for (i = msg->num_elements - 1; i >= 0; i--) {
+               attr = dsdb_attribute_by_lDAPDisplayName(dsc->schema, 
msg->elements[i].name);
+               if (ldb_attr_cmp(msg->elements[i].name, "uSNChanged") == 0) {
+                       /* Read the USN it will used at the end of the filtering
+                        * to update the max USN in the cookie if we
+                        * decide to keep this entry
+                        */
+                       val = strtoull((const 
char*)msg->elements[i].values[0].data, NULL, 0);
+                       continue;
+               }
+
+               if (ldb_attr_cmp(msg->elements[i].name,
+                                               "replPropertyMetaData") == 0) {
+                       replMetaData = (talloc_steal(dsc, 
&msg->elements[i].values[0]));
+                       continue;
+               }
+       }
+
+       if (replMetaData == NULL) {
+               bool guidfound = false;
+
+               /*
+                * We are in the case of deleted object where we don't have the
+                * right to read it.
+                */
+               if (!ldb_msg_find_attr_as_uint(msg, "isDeleted", 0)) {
+                       /*
+                        * This is not a deleted item and we don't
+                        * have the replPropertyMetaData.
+                        * Do not return it
+                        */
+                       return LDB_SUCCESS;
+               }
+               newmsg->dn = ldb_dn_new(newmsg, ldb, "");
+               if (newmsg->dn == NULL) {
+                       return ldb_oom(ldb);
+               }
+
+               el = ldb_msg_find_element(msg, "objectGUID");
+               if ( el != NULL) {
+                       guidfound = true;
+               }
+               /*
+                * We expect to find the GUID in the object,
+                * if it turns out not to be the case sometime
+                * well will uncomment the code bellow
+                */
+               SMB_ASSERT(guidfound == true);
+               /*
+               if (guidfound == false) {
+                       struct GUID guid;
+                       struct ldb_val *new_val;
+                       DATA_BLOB guid_blob;
+
+                       tmp[0] = '\0';
+                       txt = strrchr(txt, ':');
+                       if (txt == NULL) {
+                               return ldb_module_done(dsc->req, NULL, NULL, 
LDB_ERR_OPERATIONS_ERROR);
+                       }
+                       txt++;
+
+                       status = GUID_from_string(txt, &guid);
+                       if (!NT_STATUS_IS_OK(status)) {
+                               return ldb_module_done(dsc->req, NULL, NULL, 
LDB_ERR_OPERATIONS_ERROR);
+                       }
+
+                       status = GUID_to_ndr_blob(&guid, msg, &guid_blob);
+                       if (!NT_STATUS_IS_OK(status)) {
+                               return ldb_module_done(dsc->req, NULL, NULL, 
LDB_ERR_OPERATIONS_ERROR);
+                       }
+
+                       new_val = talloc(msg, struct ldb_val);
+                       if (new_val == NULL) {
+                               return ldb_oom(ldb);
+                       }
+                       new_val->data = talloc_steal(new_val, guid_blob.data);
+                       new_val->length = guid_blob.length;
+                       if (ldb_msg_add_value(msg, "objectGUID", new_val, NULL) 
!= 0) {
+                               return ldb_module_done(dsc->req, NULL, NULL, 
LDB_ERR_OPERATIONS_ERROR);
+                       }
+               }
+               */
+               ldb_msg_add(newmsg, el, LDB_FLAG_MOD_ADD);
+               talloc_steal(newmsg->elements, el->name);
+               talloc_steal(newmsg->elements, el->values);
+
+               talloc_free(msg);
+               return ldb_module_send_entry(dsc->req, msg, controls);
+       }
+
+       ndr_err = ndr_pull_struct_blob(replMetaData, dsc, &rmd,
+               (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
+       if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+               ldb_set_errstring(ldb, "Unable to unmarshall 
replPropertyMetaData");
+               return ldb_module_done(dsc->req, NULL, NULL, 
LDB_ERR_OPERATIONS_ERROR);
+       }
+       if (ldb_attr_in_list(req->op.search.attrs, "name") ||
+                       ldb_attr_in_list(req->op.search.attrs, "*")) {
+               nameasked = true;
+       }
+
+       /*
+               * If we don't have an USN and no updateness array then we skip 
the
+               * test phase this is an optimisation for the case when you
+               * first query the DC without a cookie.
+               * As this query is most probably the one
+               * that will return the biggest answer, skipping this part
+               * will really save time.
+               */
+       if (ldb_dn_compare(dsc->nc_root, msg->dn) == 0) {
+               /* If we have name then we expect to have parentGUID,
+                * it will not be the case for the root of the NC
+                */
+               delta++;
+       }
+
+       if (dsc->fromreqUSN > 0 || dsc->cursors != NULL) {
+               j = 0;
+               /*
+               * Allocate an array of size(replMetaData) of char*
+               * we know that it will be oversized but it's a short lived 
element
+               */
+               listAttr = talloc_array(msg, const char*, rmd.ctr.ctr1.count + 
1);
+               if (listAttr == NULL) {
+                       return ldb_oom(ldb);
+               }
+               for (n=0; n < rmd.ctr.ctr1.count; n++) {
+                       struct replPropertyMetaData1 *omd = 
&rmd.ctr.ctr1.array[n];
+                       if (omd->local_usn > dsc->fromreqUSN) {
+                               const struct dsdb_attribute *a = 
dsdb_attribute_by_attributeID_id(dsc->schema,
+                                                                               
omd->attid);
+                               if (!dsc->localonly) {
+                                       struct drsuapi_DsReplicaCursor *tab = 
dsc->cursors;
+                                       uint32_t l;
+                                       for (l=0; l < dsc->cursor_size; l++) {
+                                               if 
(GUID_equal(&tab[l].source_dsa_invocation_id, &omd->originating_invocation_id) 
&&
+                                                               
tab[l].highest_usn >= omd->originating_usn) {
+                                                       /*
+                                                        * If we have in the 
uptodateness vector an entry
+                                                        * with the same 
invocation id as the originating invocation
+                                                        * and if the usn in 
the vector is greater or equal to
+                                                        * the one in 
originating_usn, then it means that this entry
+                                                        * has already been 
sent (from another DC) to the client
+                                                        * no need to resend it 
one more time.
+                                                        */
+                                                       goto skip;
+                                               }
+                                       }
+                                       /* If we are here it's because we have 
a usn > (max(usn of vectors))*/
+                               }
+                               if (namereturned == false &&
+                                               nameasked == true &&
+                                               
ldb_attr_cmp(a->lDAPDisplayName, "name") == 0) {
+                                       namereturned = true;
+                                       if (ldb_dn_compare(dsc->nc_root, 
msg->dn) == 0) {
+                                               delta++;
+                                       }
+                               }
+                               listAttr[j] = a->lDAPDisplayName;
+                               j++;
+skip:
+                               continue;
+                       }
+               }
+               size = j;
+       } else {
+               size = 0;
+               if (ldb_attr_in_list(req->op.search.attrs, "*") ||
+                               ldb_attr_in_list(req->op.search.attrs, "name")) 
{
+                       namereturned = true;
+               }
+       }
+
+
+       /*
+        * Let's loop around the remaining elements
+        * to see which one are in the listAttr.
+        * If they are in this array it means that
+        * their localusn > usn from the request (in the cookie)
+        * if not we remove the attribute.
+        */
+       for (i = msg->num_elements - 1; i >= 0; i--) {
+               el = &(msg->elements[i]);
+               attr = dsdb_attribute_by_lDAPDisplayName(dsc->schema,
+                               el->name);
+               const char *ldapattrname = el->name;
+               keep = false;
+
+               if (attr->linkID & 1) {
+                       /*
+                        * Attribute is a backlink so let's remove it
+                        */
+                       continue;
+               }
+
+               if (ldb_attr_cmp(msg->elements[i].name,
+                                               "replPropertyMetaData") == 0) {
+                       continue;
+               }
+
+               if ((attr->systemFlags & (DS_FLAG_ATTR_NOT_REPLICATED | 
DS_FLAG_ATTR_IS_CONSTRUCTED))) {
+                       if (ldb_attr_cmp(attr->lDAPDisplayName, "objectGUID") 
!= 0 &&
+                                       ldb_attr_cmp(attr->lDAPDisplayName, 
"parentGUID") != 0) {
+                               /*
+                                * Attribute is constructed or not replicated, 
let's get rid of it
+                                */
+                               continue;
+                       } else {
+                               /* Let's keep the attribute that we forced to 
be added
+                                * even if they are not in the 
replicationMetaData
+                                * or are just generated
+                                */
+                               if (namereturned == false &&
+                                       (ldb_attr_cmp(attr->lDAPDisplayName, 
"parentGUID") == 0)) {
+                                       delta++;
+                                       continue;
+                               }
+                               if (ldb_msg_add(newmsg, el, LDB_FLAG_MOD_ADD) 
!= LDB_SUCCESS) {
+                                       return ldb_error(ldb,
+                                               LDB_ERR_OPERATIONS_ERROR,
+                                               "Unable to add attribute");
+                               }
+                               talloc_steal(newmsg->elements, el->name);
+                               talloc_steal(newmsg->elements, el->values);
+                               continue;
+                       }
+               }
+
+               if (ldb_attr_cmp(msg->elements[i].name, rdn) == 0) {
+                       /*
+                        * We have an attribute that is the same as the start 
of the RDN
+                        * (ie. attribute CN with rdn CN=).
+                        */
+                       continue;


-- 
Samba Shared Repository

Reply via email to