summaryrefslogtreecommitdiff
path: root/source4/dsdb/samdb/ldb_modules
diff options
context:
space:
mode:
Diffstat (limited to 'source4/dsdb/samdb/ldb_modules')
-rw-r--r--source4/dsdb/samdb/ldb_modules/acl_read.c54
-rw-r--r--source4/dsdb/samdb/ldb_modules/dirsync.c1359
-rw-r--r--source4/dsdb/samdb/ldb_modules/extended_dn_in.c31
-rw-r--r--source4/dsdb/samdb/ldb_modules/objectclass_attrs.c3
-rw-r--r--source4/dsdb/samdb/ldb_modules/proxy.c2
-rw-r--r--source4/dsdb/samdb/ldb_modules/repl_meta_data.c16
-rw-r--r--source4/dsdb/samdb/ldb_modules/ridalloc.c6
-rw-r--r--source4/dsdb/samdb/ldb_modules/rootdse.c15
-rw-r--r--source4/dsdb/samdb/ldb_modules/samba_dsdb.c1
-rw-r--r--source4/dsdb/samdb/ldb_modules/samldb.c170
-rw-r--r--source4/dsdb/samdb/ldb_modules/util.c87
-rw-r--r--source4/dsdb/samdb/ldb_modules/wscript_build9
12 files changed, 1662 insertions, 91 deletions
diff --git a/source4/dsdb/samdb/ldb_modules/acl_read.c b/source4/dsdb/samdb/ldb_modules/acl_read.c
index 181619ab28..35a840e1f4 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 0000000000..64c5047798
--- /dev/null
+++ b/source4/dsdb/samdb/ldb_modules/dirsync.c
@@ -0,0 +1,1359 @@
+/*
+ SAMDB control module
+
+ Copyright (C) Matthieu Patou <mat@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;
+ }
+
+ if (ldb_attr_cmp(attr->lDAPDisplayName, "instanceType") == 0) {
+ 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;
+ }
+ /* For links, when our functional level > windows 2000
+ * we use the RMD_LOCAL_USN information to decide wether
+ * we return the attribute or not.
+ * For windows 2000 this information is in the replPropertyMetaData
+ * so it will be handled like any other replicated attribute
+ */
+
+ if (dsc->functional_level > DS_DOMAIN_FUNCTION_2000 &&
+ attr->linkID != 0 ) {
+ int k;
+ /*
+ * Elements for incremental changes on linked attributes
+ */
+ struct ldb_message_element *el_incr_add = NULL;
+ struct ldb_message_element *el_incr_del = NULL;
+ /*
+ * Attribute is a forwardlink so let's remove it
+ */
+
+ for (k = el->num_values -1; k >= 0; k--) {
+ char *dn_ln;
+ uint32_t flags = 0;
+ uint32_t tmp_usn = 0;
+ uint32_t tmp_usn2 = 0;
+ struct GUID invocation_id = GUID_zero();
+ struct dsdb_dn *dn = dsdb_dn_parse(msg, ldb, &el->values[k], attr->syntax->ldap_oid);
+ if (dn == NULL) {
+ ldb_set_errstring(ldb, "Cannot parse DN");
+ return ldb_module_done(dsc->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
+ }
+ status = dsdb_get_extended_dn_uint32(dn->dn, &tmp_usn, "RMD_LOCAL_USN");
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(dn);
+ return ldb_module_done(dsc->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
+ }
+ status = dsdb_get_extended_dn_guid(dn->dn, &invocation_id, "RMD_INVOCID");
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(dn);
+ return ldb_module_done(dsc->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ status = dsdb_get_extended_dn_uint32(dn->dn, &flags, "RMD_FLAGS");
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(dn);
+ return ldb_module_done(dsc->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ status = dsdb_get_extended_dn_uint32(dn->dn, &tmp_usn2, "RMD_ORIGINATING_USN");
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(dn);
+ return ldb_module_done(dsc->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ ldb_dn_extended_filter(dn->dn, myaccept);
+ dn_ln = ldb_dn_get_extended_linearized(dn, dn->dn, 1);
+ if (dn_ln == NULL)
+ {
+ talloc_free(dn);
+ ldb_set_errstring(ldb, "Cannot linearize dn");
+ return ldb_module_done(dsc->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ talloc_free(el->values[k].data);
+ el->values[k].data = (uint8_t*)talloc_steal(el->values, dn_ln);
+ if (el->values[k].data == NULL) {
+ talloc_free(dn);
+ return ldb_module_done(dsc->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
+ }
+ el->values[k].length = strlen(dn_ln);
+
+
+ if (tmp_usn > dsc->fromreqUSN) {
+ 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, &invocation_id) &&
+ tab[l].highest_usn >= tmp_usn2) {
+ /*
+ * 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_link;
+ }
+ }
+ /* If we are here it's because we have a usn > (max(usn of vectors))*/
+ keep = true;
+ } else {
+ keep = true;
+ }
+ /* If we are here it's because the link is more recent than either any
+ * originating usn or local usn
+ */
+
+ if (dsc->linkIncrVal == true) {
+ struct ldb_message_element *tmpel;
+ if (flags & DSDB_RMD_FLAG_DELETED) {
+ tmpel = el_incr_del;
+ } else {
+ tmpel = el_incr_add;
+ }
+
+ if (tmpel == NULL) {
+ tmpel = talloc_zero(newmsg, struct ldb_message_element);
+ if (tmpel == NULL) {
+ return ldb_oom(ldb);
+ }
+ tmpel->values = talloc_array(tmpel, struct ldb_val, 1);
+ if (tmpel->values == NULL) {
+ return ldb_oom(ldb);
+ }
+ if (flags & DSDB_RMD_FLAG_DELETED) {
+ tmpel->name = talloc_asprintf(tmpel,
+ "%s;range=0-0",
+ el->name);
+ }
+ else {
+ tmpel->name = talloc_asprintf(tmpel,
+ "%s;range=1-1",
+ el->name);
+ }
+ if (tmpel->name == NULL) {
+ return ldb_oom(ldb);
+ }
+ tmpel->num_values = 1;
+ } else {
+ tmpel->num_values += 1;
+ tmpel->values = talloc_realloc(tmpel,
+ tmpel->values,
+ struct ldb_val,
+ tmpel->num_values);
+ if (tmpel->values == NULL) {
+ return ldb_oom(ldb);
+ }
+ tmpel = tmpel;
+ }
+ tmpel->values[tmpel->num_values -1].data =talloc_steal(tmpel->values, el->values[k].data);
+ tmpel->values[tmpel->num_values -1].length = el->values[k].length;
+
+ if (flags & DSDB_RMD_FLAG_DELETED) {
+ el_incr_del = tmpel;
+ } else {
+ el_incr_add = tmpel;
+ }
+ }
+ }
+
+ if (dsc->linkIncrVal == false) {
+ if (flags & DSDB_RMD_FLAG_DELETED) {
+ if (k < (el->num_values - 1)) {
+ memmove(el->values + k,
+ el->values + (k + 1),
+ ((el->num_values - 1) - k)*sizeof(*el->values));
+ }
+ el->num_values--;
+ }
+ }
+skip_link:
+ talloc_free(dn);
+
+ }
+ if (keep == true) {
+ if (dsc->linkIncrVal == false) {
+ 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);
+ } else {
+ if (el_incr_del) {
+ if (ldb_msg_add(newmsg, el_incr_del, LDB_FLAG_MOD_ADD))
+ return ldb_error(ldb,
+ LDB_ERR_OPERATIONS_ERROR,
+ "Unable to add attribute");
+ }
+ if (el_incr_add) {
+ if (ldb_msg_add(newmsg, el_incr_add, LDB_FLAG_MOD_ADD))
+ return ldb_error(ldb,
+ LDB_ERR_OPERATIONS_ERROR,
+ "Unable to add attribute");
+ }
+ }
+ }
+ continue;
+ }
+
+ if (listAttr) {
+ for (j=0; j<size; j++) {
+ /*
+ * We mark attribute that has already been seen well
+ * as seen. So that after attribute that are still in
+ * listAttr are attributes that has been modified after
+ * the requested USN but not present in the attributes
+ * returned by the ldb search.
+ * That is to say attributes that have been removed
+ */
+ if (listAttr[j] && ldb_attr_cmp(listAttr[j], ldapattrname) == 0) {
+ listAttr[j] = NULL;
+ keep = true;
+ continue;
+ }
+ }
+ } else {
+ keep = true;
+ }
+
+ if (keep == true) {
+ 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;
+ }
+ }
+
+ /*
+ * Here we run through the list of attributes returned
+ * in the propertyMetaData.
+ * Entries of this list have usn > requested_usn,
+ * entries that are also present in the message have been
+ * replaced by NULL, so at this moment the list contains
+ * only elements that have a usn > requested_usn and that
+ * haven't been seen. It's attributes that were removed.
+ * We add them to the message like empty elements.
+ */
+ for (j=0; j<size; j++) {
+ if (listAttr[j] && (
+ ldb_attr_in_list(req->op.search.attrs, "*") ||
+ ldb_attr_in_list(req->op.search.attrs, listAttr[j])) &&
+ (ldb_attr_cmp(listAttr[j], rdn) != 0) &&
+ (ldb_attr_cmp(listAttr[j], "instanceType") != 0)) {
+ ldb_msg_add_empty(newmsg, listAttr[j], LDB_FLAG_MOD_DELETE, NULL);
+ }
+ }
+ talloc_free(listAttr);
+
+ if ((newmsg->num_elements - ( dsc->nbDefaultAttrs - delta)) > 0) {
+ /*
+ * After cleaning attributes there is still some attributes that were not added just
+ * for the purpose of the control (objectGUID, instanceType, ...)
+ */
+
+ newmsg->dn = talloc_steal(newmsg, msg->dn);
+ if (val > dsc->highestUSN) {
+ dsc->highestUSN = val;
+ }
+ talloc_free(msg);
+ return ldb_module_send_entry(dsc->req, newmsg, controls);
+ } else {
+ talloc_free(msg);
+ return LDB_SUCCESS;
+ }
+}
+
+
+static int dirsync_create_vector(struct ldb_request *req,
+ struct ldb_reply *ares,
+ struct dirsync_context *dsc,
+ struct ldapControlDirSyncCookie *cookie,
+ struct ldb_context *ldb)
+{
+ struct ldb_result *resVector;
+ const char* attrVector[] = {"replUpToDateVector", NULL };
+ uint64_t highest_usn;
+ struct ldb_dn *nc_root;
+ uint32_t count = 1;
+ int ret;
+ struct drsuapi_DsReplicaCursor *tab;
+
+ nc_root = ldb_get_default_basedn(ldb);
+ ret = ldb_sequence_number(ldb, LDB_SEQ_HIGHEST_SEQ, &highest_usn);
+ if (ret != LDB_SUCCESS) {
+ return ldb_error(ldb, LDB_ERR_OPERATIONS_ERROR, "Unable to get highest USN from current NC");
+ }
+
+ /* If we have a full answer then the highest USN
+ * is not the highest USN from the result set but the
+ * highest of the naming context, unless the sequence is not updated yet.
+ */
+ if (highest_usn > dsc->highestUSN) {
+ dsc->highestUSN = highest_usn;
+ }
+
+
+ ret = dsdb_module_search_dn(dsc->module, dsc, &resVector,
+ nc_root,
+ attrVector,
+ DSDB_FLAG_NEXT_MODULE, req);
+
+ if (resVector->count != 0) {
+ DATA_BLOB blob;
+ uint32_t i;
+ struct ldb_message_element *el = ldb_msg_find_element(resVector->msgs[0], "replUpToDateVector");
+ if (el) {
+ enum ndr_err_code ndr_err;
+ struct replUpToDateVectorBlob utd;
+ blob.data = el->values[0].data;
+ blob.length = el->values[0].length;
+ ndr_err = ndr_pull_struct_blob(&blob, dsc, &utd,
+ (ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob);
+
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return ldb_error(ldb, LDB_ERR_OPERATIONS_ERROR,
+ "Unable to pull replUpToDateVectorBlob structure");
+ }
+
+
+ count += utd.ctr.ctr2.count;
+ tab = talloc_array(cookie, struct drsuapi_DsReplicaCursor, count);
+ if (tab == NULL) {
+ return ldb_oom(ldb);
+ }
+ for (i=1; i < count; i++) {
+ memset(&tab[i], 0, sizeof(struct drsuapi_DsReplicaCursor));
+ tab[i].highest_usn = utd.ctr.ctr2.cursors[i-1].highest_usn;
+ tab[i].source_dsa_invocation_id = utd.ctr.ctr2.cursors[i-1].source_dsa_invocation_id;
+ }
+ } else {
+ tab = talloc_array(cookie, struct drsuapi_DsReplicaCursor, count);
+ if (tab == NULL) {
+ return ldb_oom(ldb);
+ }
+ }
+ } else {
+ /*
+ * No replUpToDateVector ? it happens quite often (1 DC,
+ * other DCs didn't update ...
+ */
+ tab = talloc_array(cookie, struct drsuapi_DsReplicaCursor, count);
+ if (tab == NULL) {
+ return ldb_oom(ldb);
+ }
+ }
+ /* Our vector is always the first */
+ tab[0].highest_usn = dsc->highestUSN;
+ tab[0].source_dsa_invocation_id = *(dsc->our_invocation_id);
+
+
+ /* We have to add the updateness vector that we have*/
+ /* Version is always 1 in dirsync cookies */
+ cookie->blob.extra.uptodateness_vector.version = 1;
+ cookie->blob.extra.uptodateness_vector.reserved = 0;
+ cookie->blob.extra.uptodateness_vector.ctr.ctr1.count = count;
+ cookie->blob.extra.uptodateness_vector.ctr.ctr1.reserved = 0;
+ cookie->blob.extra.uptodateness_vector.ctr.ctr1.cursors = tab;
+
+ return LDB_SUCCESS;
+}
+
+static int dirsync_search_callback(struct ldb_request *req, struct ldb_reply *ares)
+{
+ int ret;
+ struct dirsync_context *dsc;
+ struct ldb_result *res, *res2;
+ struct ldb_dirsync_control *control;
+ struct ldapControlDirSyncCookie *cookie;
+ struct ldb_context *ldb;
+ struct ldb_dn *dn;
+ struct ldb_val *val;
+ DATA_BLOB *blob;
+ NTTIME now;
+ const char *attrs[] = { "objectGUID", NULL };
+ enum ndr_err_code ndr_err;
+ char *tmp;
+ uint32_t flags;
+
+ dsc = talloc_get_type_abort(req->context, struct dirsync_context);
+ ldb = ldb_module_get_ctx(dsc->module);
+ if (!ares) {
+ return ldb_module_done(dsc->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_module_done(dsc->req, ares->controls,
+ ares->response, ares->error);
+ }
+
+ switch (ares->type) {
+ case LDB_REPLY_ENTRY:
+ return dirsync_filter_entry(req, ares->message, ares->controls, dsc, false);
+
+ case LDB_REPLY_REFERRAL:
+ /* Skip the ldap(s):// so up to 8 chars,
+ * we don't care to be precise as the goal is to be in
+ * the name of DC, then we search the next '/'
+ * as it will be the last char before the DN of the referal
+ */
+ if (strncmp(ares->referral, "ldap://", 7) == 0) {
+ tmp = ares->referral + 7;
+ } else if (strncmp(ares->referral, "ldaps://", 8) == 0) {
+ tmp = ares->referral + 8;
+ } else {
+ return ldb_operr(ldb);
+ }
+
+ tmp = strchr(tmp, '/');
+ tmp++;
+
+ dn = ldb_dn_new(dsc, ldb, tmp);
+ if (dn == NULL) {
+ return ldb_oom(ldb);
+ }
+
+ flags = DSDB_FLAG_NEXT_MODULE |
+ DSDB_RMD_FLAG_DELETED |
+ DSDB_SEARCH_SHOW_EXTENDED_DN;
+
+ if (dsc->assystem) {
+ flags = flags | DSDB_FLAG_AS_SYSTEM;
+ }
+
+ ret = dsdb_module_search_tree(dsc->module, dsc, &res,
+ dn, LDB_SCOPE_BASE,
+ req->op.search.tree,
+ req->op.search.attrs,
+ flags, req);
+
+ if (ret != LDB_SUCCESS) {
+ talloc_free(dn);
+ return ret;
+ }
+
+ if (res->count > 1) {
+ char *ldbmsg = talloc_asprintf(dn, "LDB returned more than result for dn: %s", tmp);
+ if (ldbmsg) {
+ ldb_set_errstring(ldb, ldbmsg);
+ }
+ talloc_free(dn);
+ return ldb_module_done(dsc->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
+ } else if (res->count == 0) {
+ /* if nothing is returned then it means that we don't
+ * have access to it.
+ */
+ return LDB_SUCCESS;
+ }
+
+ talloc_free(dn);
+ /*
+ * Fetch the objectGUID of the root of current NC
+ */
+ ret = dsdb_module_search_dn(dsc->module, dsc, &res2,
+ req->op.search.base,
+ attrs,
+ DSDB_FLAG_NEXT_MODULE, req);
+
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ if (res2->msgs[0]->num_elements != 1) {
+ ldb_set_errstring(ldb,
+ "More than 1 attribute returned while looking for objectGUID");
+ return ldb_module_done(dsc->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ val = res2->msgs[0]->elements[0].values;
+ ret = ldb_msg_add_value(res->msgs[0], "parentGUID", val, NULL);
+ /*
+ * It *very* important to steal otherwise as val is in a subcontext
+ * related to res2, when the value will be one more time stolen
+ * it's elements[x].values that will be stolen, so it's important to
+ * recreate the context hierrachy as if it was done from a ldb_request
+ */
+ talloc_steal(res->msgs[0]->elements[0].values, val);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ return dirsync_filter_entry(req, res->msgs[0], res->controls, dsc, true);
+
+ case LDB_REPLY_DONE:
+ /*
+ * Let's add our own control
+ */
+
+ control = talloc_zero(ares->controls, struct ldb_dirsync_control);
+ if (control == NULL) {
+ return ldb_oom(ldb);
+ }
+
+ /*
+ * When outputing flags is used to say more results.
+ * For the moment we didn't honnor the size info */
+
+ control->flags = 0;
+
+ /*
+ * max_attribute is unused cf. 3.1.1.3.4.1.3 LDAP_SERVER_DIRSYNC_OID in MS-ADTS
+ */
+
+ control->max_attributes = 0;
+ cookie = talloc_zero(control, struct ldapControlDirSyncCookie);
+ if (cookie == NULL) {
+ return ldb_oom(ldb);
+ }
+
+ if (!dsc->partial) {
+ ret = dirsync_create_vector(req, ares, dsc, cookie, ldb);
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(dsc->req, NULL, NULL, ret);
+ }
+ }
+
+ unix_to_nt_time(&now, time(NULL));
+ cookie->blob.time = now;
+ cookie->blob.highwatermark.highest_usn = dsc->highestUSN;
+ cookie->blob.highwatermark.tmp_highest_usn = dsc->highestUSN;
+ cookie->blob.guid1 = *(dsc->our_invocation_id);
+
+ blob = talloc_zero(control, DATA_BLOB);
+ if (blob == NULL) {
+ return ldb_oom(ldb);
+ }
+
+ ndr_err = ndr_push_struct_blob(blob, blob, cookie,
+ (ndr_push_flags_fn_t)ndr_push_ldapControlDirSyncCookie);
+
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ ldb_set_errstring(ldb, "Can't marshall ldapControlDirSyncCookie struct");
+ return ldb_module_done(dsc->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
+ }
+ control->cookie = (char *)blob->data;
+ control->cookie_len = blob->length;
+ ldb_reply_add_control(ares, LDB_CONTROL_DIRSYNC_OID, true, control);
+
+ return ldb_module_done(dsc->req, ares->controls,
+ ares->response, LDB_SUCCESS);
+
+ }
+ return LDB_SUCCESS;
+}
+
+static int dirsync_ldb_search(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_control *control;
+ struct ldb_result *acl_res;
+ struct ldb_dirsync_control *dirsync_ctl;
+ struct ldb_request *down_req;
+ struct dirsync_context *dsc;
+ struct ldb_context *ldb;
+ struct ldb_parse_tree *new_tree = req->op.search.tree;
+ uint32_t flags = 0;
+ enum ndr_err_code ndr_err;
+ DATA_BLOB blob;
+ const char **attrs;
+ int ret;
+
+
+ if (ldb_dn_is_special(req->op.search.base)) {
+ return ldb_next_request(module, req);
+ }
+
+ /*
+ * check if there's an extended dn control
+ */
+ control = ldb_request_get_control(req, LDB_CONTROL_DIRSYNC_OID);
+ if (control == NULL) {
+ /* not found go on */
+ return ldb_next_request(module, req);
+ }
+
+ ldb = ldb_module_get_ctx(module);
+ /*
+ * This control must always be critical otherwise we return PROTOCOL error
+ */
+ if (!control->critical) {
+ return ldb_operr(ldb);
+ }
+
+ dsc = talloc_zero(req, struct dirsync_context);
+ if (dsc == NULL) {
+ return ldb_oom(ldb);
+ }
+ dsc->module = module;
+ dsc->req = req;
+ dsc->nbDefaultAttrs = 0;
+
+
+ dirsync_ctl = talloc_get_type(control->data, struct ldb_dirsync_control);
+ if (dirsync_ctl == NULL) {
+ return ldb_error(ldb, LDB_ERR_PROTOCOL_ERROR, "No data in dirsync control");
+ }
+
+ ret = dsdb_find_nc_root(ldb, dsc, req->op.search.base, &dsc->nc_root);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ if (ldb_dn_compare(dsc->nc_root, req->op.search.base) != 0) {
+ if (dirsync_ctl->flags & LDAP_DIRSYNC_OBJECT_SECURITY) {
+ return ldb_error(ldb, LDB_ERR_UNWILLING_TO_PERFORM,
+ "DN is not one of the naming context");
+ }
+ else {
+ return ldb_error(ldb, LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS,
+ "dN is not one of the naming context");
+ }
+ }
+
+ if (!(dirsync_ctl->flags & LDAP_DIRSYNC_OBJECT_SECURITY)) {
+ struct dom_sid *sid;
+ struct security_descriptor *sd = NULL;
+ const char *acl_attrs[] = { "nTSecurityDescriptor", "objectSid", NULL };
+ /*
+ * If we don't have the flag and if we have the "replicate directory change" granted
+ * then we upgrade ourself to system to not be blocked by the acl
+ */
+ /* FIXME we won't check the replicate directory change filtered attribute set
+ * it should be done so that if attr is not empty then we check that the user
+ * has also this right
+ */
+
+ /*
+ * First change to system to get the SD of the root of current NC
+ * if we don't the acl_read will forbid us the right to read it ...
+ */
+ ret = dsdb_module_search_dn(module, dsc, &acl_res,
+ req->op.search.base,
+ acl_attrs,
+ DSDB_FLAG_NEXT_MODULE|DSDB_FLAG_AS_SYSTEM, req);
+
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ sid = samdb_result_dom_sid(dsc, acl_res->msgs[0], "objectSid");
+ /* sid can be null ... */
+ ret = dsdb_get_sd_from_ldb_message(ldb_module_get_ctx(module), acl_res, acl_res->msgs[0], &sd);
+
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ ret = acl_check_extended_right(dsc, sd, acl_user_token(module), GUID_DRS_GET_CHANGES, SEC_ADS_CONTROL_ACCESS, sid);
+
+ if (ret == LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS) {
+ return ret;
+ }
+ dsc->assystem = true;
+ ret = ldb_request_add_control(req, LDB_CONTROL_AS_SYSTEM_OID, false, NULL);
+
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ talloc_free(acl_res);
+ } else {
+ flags |= DSDB_ACL_CHECKS_DIRSYNC_FLAG;
+
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ }
+
+ dsc->functional_level = dsdb_functional_level(ldb);
+
+ if (req->op.search.attrs) {
+ attrs = ldb_attr_list_copy(dsc, req->op.search.attrs);
+ if (attrs == NULL) {
+ return ldb_oom(ldb);
+ }
+ /*
+ * Check if we have only "dn" as attribute, if so then
+ * treat as if "*" was requested
+ */
+ if (attrs && attrs[0]) {
+ if (ldb_attr_cmp(attrs[0], "dn") == 0 && !attrs[1]) {
+ attrs = talloc_array(dsc, const char*, 2);
+ if (attrs == NULL) {
+ return ldb_oom(ldb);
+ }
+ attrs[0] = "*";
+ attrs[1] = NULL;
+ }
+ }
+ /*
+ * When returning all the attributes return also the SD as
+ * Windws do so.
+ */
+ if (ldb_attr_in_list(attrs, "*")) {
+ struct ldb_sd_flags_control *sdctr = talloc_zero(dsc, struct ldb_sd_flags_control);
+ sdctr->secinfo_flags = 0;
+ ret = ldb_request_add_control(req, LDB_CONTROL_SD_FLAGS_OID, false, sdctr);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ attrs = ldb_attr_list_copy_add(dsc, attrs, "parentGUID");
+ if (attrs == NULL) {
+ return ldb_oom(ldb);
+ }
+ attrs = ldb_attr_list_copy_add(dsc, attrs, "replPropertyMetaData");
+ if (attrs == NULL) {
+ return ldb_oom(ldb);
+ }
+ /*
+ * When no attributes are asked we in anycase expect at least 3 attributes:
+ * * instanceType
+ * * objectGUID
+ * * parentGUID
+ */
+
+ dsc->nbDefaultAttrs = 3;
+ } else {
+ /*
+ * We will need this two attributes in the callback
+ */
+ attrs = ldb_attr_list_copy_add(dsc, attrs, "usnChanged");
+ if (attrs == NULL) {
+ return ldb_operr(ldb);
+ }
+ attrs = ldb_attr_list_copy_add(dsc, attrs, "replPropertyMetaData");
+ if (attrs == NULL) {
+ return ldb_operr(ldb);
+ }
+
+ if (!ldb_attr_in_list(attrs, "instanceType")) {
+ attrs = ldb_attr_list_copy_add(dsc, attrs, "instanceType");
+ if (attrs == NULL) {
+ return ldb_operr(ldb);
+ }
+ dsc->nbDefaultAttrs++;
+ }
+
+ if (!ldb_attr_in_list(attrs, "objectGUID")) {
+ attrs = ldb_attr_list_copy_add(dsc, attrs, "objectGUID");
+ if (attrs == NULL) {
+ return ldb_operr(ldb);
+ }
+ }
+ /*
+ * Always increment the number of asked attributes as we don't care if objectGUID was asked
+ * or not for counting the number of "real" attributes returned.
+ */
+ dsc->nbDefaultAttrs++;
+
+ if (!ldb_attr_in_list(attrs, "parentGUID")) {
+ attrs = ldb_attr_list_copy_add(dsc, attrs, "parentGUID");
+ if (attrs == NULL) {
+ return ldb_operr(ldb);
+ }
+ }
+ dsc->nbDefaultAttrs++;
+
+ }
+ } else {
+ struct ldb_sd_flags_control *sdctr = talloc_zero(dsc, struct ldb_sd_flags_control);
+ sdctr->secinfo_flags = 0;
+ ret = ldb_request_add_control(req, LDB_CONTROL_SD_FLAGS_OID, false, sdctr);
+ attrs = talloc_array(dsc, const char*, 4);
+ if (attrs == NULL) {
+ return ldb_operr(ldb);
+ }
+ attrs[0] = "*";
+ attrs[1] = "parentGUID";
+ attrs[2] = "replPropertyMetaData";
+ attrs[3] = NULL;
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ /*
+ * When no attributes are asked we in anycase expect at least 3 attributes:
+ * * instanceType
+ * * objectGUID
+ * * parentGUID
+ */
+
+ dsc->nbDefaultAttrs = 3;
+ }
+
+ if (!ldb_request_get_control(req, LDB_CONTROL_EXTENDED_DN_OID)) {
+ ret = ldb_request_add_control(req, LDB_CONTROL_EXTENDED_DN_OID, false, NULL);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ dsc->noextended = true;
+ }
+
+ if (ldb_request_get_control(req, LDB_CONTROL_REVEAL_INTERNALS) == NULL) {
+ ret = ldb_request_add_control(req, LDB_CONTROL_REVEAL_INTERNALS, false, NULL);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+
+ if (ldb_request_get_control(req, LDB_CONTROL_SHOW_RECYCLED_OID) == NULL) {
+ ret = ldb_request_add_control(req, LDB_CONTROL_SHOW_RECYCLED_OID, false, NULL);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+
+ if (ldb_request_get_control(req, LDB_CONTROL_SHOW_DELETED_OID) == NULL) {
+ ret = ldb_request_add_control(req, LDB_CONTROL_SHOW_DELETED_OID, false, NULL);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+
+ if (dirsync_ctl->flags & LDAP_DIRSYNC_INCREMENTAL_VALUES) {
+ dsc->linkIncrVal = true;
+ } else {
+ dsc->linkIncrVal = false;
+ }
+
+ dsc->our_invocation_id = samdb_ntds_invocation_id(ldb);
+ if (dsc->our_invocation_id == NULL) {
+ return ldb_operr(ldb);
+ }
+
+ if (dirsync_ctl->cookie_len > 0) {
+ struct ldapControlDirSyncCookie cookie;
+
+ blob.data = (uint8_t *)dirsync_ctl->cookie;
+ blob.length = dirsync_ctl->cookie_len;
+ ndr_err = ndr_pull_struct_blob(&blob, dsc, &cookie,
+ (ndr_pull_flags_fn_t)ndr_pull_ldapControlDirSyncCookie);
+
+ /* If we can't unmarshall the cookie into the correct structure we return
+ * unsupported critical extension
+ */
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return ldb_error(ldb, LDB_ERR_UNSUPPORTED_CRITICAL_EXTENSION,
+ "Unable to unmarshall cookie as a ldapControlDirSyncCookie structure");
+ }
+
+ /*
+ * Let's search for the max usn withing the cookie
+ */
+ if (GUID_equal(&(cookie.blob.guid1), dsc->our_invocation_id)) {
+ /*
+ * Ok, it's our invocation ID so we can treat the demand
+ * Let's take the highest usn from (tmp)highest_usn
+ */
+ dsc->fromreqUSN = cookie.blob.highwatermark.tmp_highest_usn;
+ dsc->localonly = true;
+
+ if (cookie.blob.highwatermark.highest_usn > cookie.blob.highwatermark.tmp_highest_usn) {
+ dsc->fromreqUSN = cookie.blob.highwatermark.highest_usn;
+ }
+ } else {
+ dsc->localonly = false;
+ }
+ if (cookie.blob.extra_length > 0 &&
+ cookie.blob.extra.uptodateness_vector.ctr.ctr1.count > 0) {
+ struct drsuapi_DsReplicaCursor cursor;
+ uint32_t p;
+ for (p=0; p < cookie.blob.extra.uptodateness_vector.ctr.ctr1.count; p++) {
+ cursor = cookie.blob.extra.uptodateness_vector.ctr.ctr1.cursors[p];
+ if (GUID_equal( &(cursor.source_dsa_invocation_id), dsc->our_invocation_id)) {
+ if (cursor.highest_usn > dsc->fromreqUSN) {
+ dsc->fromreqUSN = cursor.highest_usn;
+ }
+ }
+ }
+ dsc->cursors = talloc_steal(dsc,
+ cookie.blob.extra.uptodateness_vector.ctr.ctr1.cursors);
+ if (dsc->cursors == NULL) {
+ return ldb_oom(ldb);
+ }
+ dsc->cursor_size = p;
+ }
+ }
+
+ DEBUG(4, ("Dirsync: searching with min usn > %llu\n",
+ (long long unsigned int)dsc->fromreqUSN));
+ if (dsc->fromreqUSN > 0) {
+ /* FIXME it would be better to use PRId64 */
+ char *expression = talloc_asprintf(dsc, "(&%s(uSNChanged>=%llu))",
+ ldb_filter_from_tree(dsc,
+ req->op.search.tree),
+ (long long unsigned int)(dsc->fromreqUSN + 1));
+
+ if (expression == NULL) {
+ return ldb_oom(ldb);
+ }
+ new_tree = ldb_parse_tree(req, expression);
+ if (new_tree == NULL) {
+ return ldb_error(ldb, LDB_ERR_OPERATIONS_ERROR,
+ "Problem while parsing tree");
+ }
+
+ }
+ /*
+ * Remove our control from the list of controls
+ */
+ if (!ldb_save_controls(control, req, NULL)) {
+ return ldb_operr(ldb);
+ }
+ dsc->schema = dsdb_get_schema(ldb, dsc);
+ /*
+ * At the begining we make the hypothesis that we will return a complete
+ * result set
+ */
+
+ dsc->partial = false;
+
+ /*
+ * 3.1.1.3.4.1.3 of MS-ADTS.pdf specify that if the scope is not subtree
+ * we treat the search as if subtree was specified
+ */
+
+ ret = ldb_build_search_req_ex(&down_req, ldb, dsc,
+ req->op.search.base,
+ LDB_SCOPE_SUBTREE,
+ new_tree,
+ attrs,
+ req->controls,
+ dsc, dirsync_search_callback,
+ req);
+ ldb_req_set_custom_flags(down_req, flags);
+ LDB_REQ_SET_LOCATION(down_req);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ /* perform the search */
+ return ldb_next_request(module, down_req);
+}
+
+static int dirsync_ldb_init(struct ldb_module *module)
+{
+ int ret;
+
+ ret = ldb_mod_register_control(module, LDB_CONTROL_DIRSYNC_OID);
+ if (ret != LDB_SUCCESS) {
+ ldb_debug(ldb_module_get_ctx(module), LDB_DEBUG_ERROR,
+ "dirsync: Unable to register control with rootdse!\n");
+ return ldb_operr(ldb_module_get_ctx(module));
+ }
+
+ return ldb_next_init(module);
+}
+
+static const struct ldb_module_ops ldb_dirsync_ldb_module_ops = {
+ .name = "dirsync",
+ .search = dirsync_ldb_search,
+ .init_context = dirsync_ldb_init,
+};
+
+/*
+ initialise the module
+ */
+_PUBLIC_ int ldb_dirsync_module_init(const char *version)
+{
+ int ret;
+ LDB_MODULE_CHECK_VERSION(version);
+ ret = ldb_register_module(&ldb_dirsync_ldb_module_ops);
+ return ret;
+}
diff --git a/source4/dsdb/samdb/ldb_modules/extended_dn_in.c b/source4/dsdb/samdb/ldb_modules/extended_dn_in.c
index 3e2004d6f3..9a70d9a3db 100644
--- a/source4/dsdb/samdb/ldb_modules/extended_dn_in.c
+++ b/source4/dsdb/samdb/ldb_modules/extended_dn_in.c
@@ -103,6 +103,18 @@ static int extended_base_callback(struct ldb_request *req, struct ldb_reply *are
switch (ares->type) {
case LDB_REPLY_ENTRY:
+ if (ac->basedn) {
+ /* we have more than one match! This can
+ happen as S-1-5-17 appears twice in a
+ normal provision. We need to return
+ NO_SUCH_OBJECT */
+ const char *str = talloc_asprintf(req, "Duplicate base-DN matches found for '%s'",
+ ldb_dn_get_extended_linearized(req, ac->req->op.search.base, 1));
+ ldb_set_errstring(ldb_module_get_ctx(ac->module), str);
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_NO_SUCH_OBJECT);
+ }
+
if (!ac->wellknown_object) {
ac->basedn = talloc_steal(ac, ares->message->dn);
break;
@@ -303,30 +315,33 @@ static int extended_dn_in_fix(struct ldb_module *module, struct ldb_request *req
guid_val = ldb_dn_get_extended_component(dn, "GUID");
wkguid_val = ldb_dn_get_extended_component(dn, "WKGUID");
- if (sid_val) {
+ /*
+ prioritise the GUID - we have had instances of
+ duplicate SIDs in the database in the
+ ForeignSecurityPrinciples due to provision errors
+ */
+ if (guid_val) {
all_partitions = true;
base_dn = ldb_get_default_basedn(ldb_module_get_ctx(module));
- base_dn_filter = talloc_asprintf(req, "(objectSid=%s)",
- ldb_binary_encode(req, *sid_val));
+ base_dn_filter = talloc_asprintf(req, "(objectGUID=%s)",
+ ldb_binary_encode(req, *guid_val));
if (!base_dn_filter) {
return ldb_oom(ldb_module_get_ctx(module));
}
base_dn_scope = LDB_SCOPE_SUBTREE;
base_dn_attrs = no_attr;
- } else if (guid_val) {
-
+ } else if (sid_val) {
all_partitions = true;
base_dn = ldb_get_default_basedn(ldb_module_get_ctx(module));
- base_dn_filter = talloc_asprintf(req, "(objectGUID=%s)",
- ldb_binary_encode(req, *guid_val));
+ base_dn_filter = talloc_asprintf(req, "(objectSid=%s)",
+ ldb_binary_encode(req, *sid_val));
if (!base_dn_filter) {
return ldb_oom(ldb_module_get_ctx(module));
}
base_dn_scope = LDB_SCOPE_SUBTREE;
base_dn_attrs = no_attr;
-
} else if (wkguid_val) {
char *wkguid_dup;
char *tail_str;
diff --git a/source4/dsdb/samdb/ldb_modules/objectclass_attrs.c b/source4/dsdb/samdb/ldb_modules/objectclass_attrs.c
index 9df121002f..5639a7a3e3 100644
--- a/source4/dsdb/samdb/ldb_modules/objectclass_attrs.c
+++ b/source4/dsdb/samdb/ldb_modules/objectclass_attrs.c
@@ -140,7 +140,8 @@ static int attr_handler(struct oc_context *ac)
if (!(msg->elements[i].flags & LDB_FLAG_INTERNAL_DISABLE_VALIDATION)) {
werr = attr->syntax->validate_ldb(&syntax_ctx, attr,
&msg->elements[i]);
- if (!W_ERROR_IS_OK(werr)) {
+ if (!W_ERROR_IS_OK(werr) &&
+ !ldb_request_get_control(ac->req, LDB_CONTROL_RELAX_OID)) {
ldb_asprintf_errstring(ldb, "objectclass_attrs: attribute '%s' on entry '%s' contains at least one invalid value!",
msg->elements[i].name,
ldb_dn_get_linearized(msg->dn));
diff --git a/source4/dsdb/samdb/ldb_modules/proxy.c b/source4/dsdb/samdb/ldb_modules/proxy.c
index 6fba24fc2d..5f6e56f9d4 100644
--- a/source4/dsdb/samdb/ldb_modules/proxy.c
+++ b/source4/dsdb/samdb/ldb_modules/proxy.c
@@ -138,7 +138,7 @@ static int load_proxy_info(struct ldb_module *module)
ldb_set_opaque(proxy->upstream, "credentials", creds);
ret = ldb_connect(proxy->upstream, url, 0, NULL);
- if (ret != 0) {
+ if (ret != LDB_SUCCESS) {
ldb_debug(ldb, LDB_DEBUG_FATAL, "proxy failed to connect to %s\n", url);
goto failed;
}
diff --git a/source4/dsdb/samdb/ldb_modules/repl_meta_data.c b/source4/dsdb/samdb/ldb_modules/repl_meta_data.c
index 646abeb771..9d2e5e2ac3 100644
--- a/source4/dsdb/samdb/ldb_modules/repl_meta_data.c
+++ b/source4/dsdb/samdb/ldb_modules/repl_meta_data.c
@@ -254,7 +254,16 @@ static int replmd_process_backlink(struct ldb_module *module, struct la_backlink
msg->elements[0].flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE, parent);
- if (ret != LDB_SUCCESS) {
+ if (ret == LDB_ERR_NO_SUCH_ATTRIBUTE && !bl->active) {
+ /* we allow LDB_ERR_NO_SUCH_ATTRIBUTE as success to
+ cope with possible corruption where the backlink has
+ already been removed */
+ DEBUG(0,("WARNING: backlink from %s already removed from %s - %s\n",
+ ldb_dn_get_linearized(target_dn),
+ ldb_dn_get_linearized(source_dn),
+ ldb_errstring(ldb)));
+ ret = LDB_SUCCESS;
+ } else if (ret != LDB_SUCCESS) {
ldb_asprintf_errstring(ldb, "Failed to %s backlink from %s to %s - %s",
bl->active?"add":"remove",
ldb_dn_get_linearized(source_dn),
@@ -1634,7 +1643,8 @@ static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct d
if (old_addtime == NULL) {
old_addtime = &tval;
}
- if (dsdb_dn != old_dsdb_dn) {
+ if (dsdb_dn != old_dsdb_dn ||
+ ldb_dn_get_extended_component(dn, "RMD_ADDTIME") == NULL) {
ret = ldb_dn_set_extended_component(dn, "RMD_ADDTIME", old_addtime);
if (ret != LDB_SUCCESS) return ret;
}
@@ -2488,7 +2498,7 @@ static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *are
if (ret == LDB_ERR_REFERRAL) {
struct ldb_dn *olddn = ac->req->op.rename.olddn;
struct loadparm_context *lp_ctx;
- const char *referral;
+ char *referral;
lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
struct loadparm_context);
diff --git a/source4/dsdb/samdb/ldb_modules/ridalloc.c b/source4/dsdb/samdb/ldb_modules/ridalloc.c
index 5051919672..28fade11b1 100644
--- a/source4/dsdb/samdb/ldb_modules/ridalloc.c
+++ b/source4/dsdb/samdb/ldb_modules/ridalloc.c
@@ -66,14 +66,14 @@
*/
static void ridalloc_poke_rid_manager(struct ldb_module *module)
{
- struct messaging_context *msg;
+ struct imessaging_context *msg;
struct server_id *server;
struct ldb_context *ldb = ldb_module_get_ctx(module);
struct loadparm_context *lp_ctx =
(struct loadparm_context *)ldb_get_opaque(ldb, "loadparm");
TALLOC_CTX *tmp_ctx = talloc_new(module);
- msg = messaging_client_init(tmp_ctx, lpcfg_messaging_path(tmp_ctx, lp_ctx),
+ msg = imessaging_client_init(tmp_ctx, lpcfg_imessaging_path(tmp_ctx, lp_ctx),
ldb_get_event_context(ldb));
if (!msg) {
DEBUG(3,(__location__ ": Failed to create messaging context\n"));
@@ -88,7 +88,7 @@ static void ridalloc_poke_rid_manager(struct ldb_module *module)
return;
}
- messaging_send(msg, server[0], MSG_DREPL_ALLOCATE_RID, NULL);
+ imessaging_send(msg, server[0], MSG_DREPL_ALLOCATE_RID, NULL);
/* we don't care if the message got through */
talloc_free(tmp_ctx);
diff --git a/source4/dsdb/samdb/ldb_modules/rootdse.c b/source4/dsdb/samdb/ldb_modules/rootdse.c
index 0fd65f4795..c584a11b2c 100644
--- a/source4/dsdb/samdb/ldb_modules/rootdse.c
+++ b/source4/dsdb/samdb/ldb_modules/rootdse.c
@@ -222,11 +222,10 @@ static int rootdse_add_dynamic(struct ldb_module *module, struct ldb_message *ms
struct loadparm_context);
char *ldap_service_name, *hostname;
- hostname = talloc_strdup(msg, lpcfg_netbios_name(lp_ctx));
+ hostname = strlower_talloc(msg, lpcfg_netbios_name(lp_ctx));
if (hostname == NULL) {
goto failed;
}
- strlower_m(hostname);
ldap_service_name = talloc_asprintf(msg, "%s:%s$@%s",
samdb_forest_name(ldb, msg),
@@ -613,7 +612,11 @@ static int rootdse_filter_controls(struct ldb_module *module, struct ldb_request
continue;
}
- if (is_registered) {
+ /* If the control is DIRSYNC control then we keep the critical
+ * flag as the dirsync module will need to act upon it
+ */
+ if (is_registered && strcmp(req->controls[i]->oid,
+ LDB_CONTROL_DIRSYNC_OID)!= 0) {
req->controls[i]->critical = 0;
}
}
@@ -1195,7 +1198,7 @@ static int rootdse_become_master(struct ldb_module *module,
struct ldb_request *req,
enum drepl_role_master role)
{
- struct messaging_context *msg;
+ struct imessaging_context *msg;
struct ldb_context *ldb = ldb_module_get_ctx(module);
TALLOC_CTX *tmp_ctx = talloc_new(req);
struct loadparm_context *lp_ctx = ldb_get_opaque(ldb, "loadparm");
@@ -1223,10 +1226,10 @@ static int rootdse_become_master(struct ldb_module *module,
"RODC cannot become a role master.");
}
- msg = messaging_client_init(tmp_ctx, lpcfg_messaging_path(tmp_ctx, lp_ctx),
+ msg = imessaging_client_init(tmp_ctx, lpcfg_imessaging_path(tmp_ctx, lp_ctx),
ldb_get_event_context(ldb));
if (!msg) {
- ldb_asprintf_errstring(ldb, "Failed to generate client messaging context in %s", lpcfg_messaging_path(tmp_ctx, lp_ctx));
+ ldb_asprintf_errstring(ldb, "Failed to generate client messaging context in %s", lpcfg_imessaging_path(tmp_ctx, lp_ctx));
return LDB_ERR_OPERATIONS_ERROR;
}
irpc_handle = irpc_binding_handle_by_name(tmp_ctx, msg,
diff --git a/source4/dsdb/samdb/ldb_modules/samba_dsdb.c b/source4/dsdb/samdb/ldb_modules/samba_dsdb.c
index 35b323b72f..e4de1524be 100644
--- a/source4/dsdb/samdb/ldb_modules/samba_dsdb.c
+++ b/source4/dsdb/samdb/ldb_modules/samba_dsdb.c
@@ -163,6 +163,7 @@ static int samba_dsdb_init(struct ldb_module *module)
static const char *modules_list[] = {"resolve_oids",
"rootdse",
"lazy_commit",
+ "dirsync",
"paged_results",
"ranged_results",
"anr",
diff --git a/source4/dsdb/samdb/ldb_modules/samldb.c b/source4/dsdb/samdb/ldb_modules/samldb.c
index 21341850d9..6533d1006b 100644
--- a/source4/dsdb/samdb/ldb_modules/samldb.c
+++ b/source4/dsdb/samdb/ldb_modules/samldb.c
@@ -3,7 +3,7 @@
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
Copyright (C) Simo Sorce 2004-2008
- Copyright (C) Matthias Dieter Wallnöfer 2009-2010
+ Copyright (C) Matthias Dieter Wallnöfer 2009-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
@@ -790,6 +790,8 @@ static int samldb_schema_info_update(struct samldb_ctx *ac)
return LDB_SUCCESS;
}
+static int samldb_prim_group_tester(struct samldb_ctx *ac, uint32_t rid);
+
/*
* "Objectclass" trigger (MS-SAMR 3.1.1.8.1)
*
@@ -801,10 +803,9 @@ static int samldb_schema_info_update(struct samldb_ctx *ac)
static int samldb_objectclass_trigger(struct samldb_ctx *ac)
{
struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
- struct loadparm_context *lp_ctx = talloc_get_type(ldb_get_opaque(ldb,
- "loadparm"), struct loadparm_context);
+ void *skip_allocate_sids = ldb_get_opaque(ldb,
+ "skip_allocate_sids");
struct ldb_message_element *el, *el2;
- enum sid_generator sid_generator;
struct dom_sid *sid;
int ret;
@@ -830,12 +831,9 @@ static int samldb_objectclass_trigger(struct samldb_ctx *ac)
}
/* but generate a new SID when we do have an add operations */
- if ((sid == NULL) && (ac->req->operation == LDB_ADD)) {
- sid_generator = lpcfg_sid_generator(lp_ctx);
- if (sid_generator == SID_GENERATOR_INTERNAL) {
- ret = samldb_add_step(ac, samldb_allocate_sid);
- if (ret != LDB_SUCCESS) return ret;
- }
+ if ((sid == NULL) && (ac->req->operation == LDB_ADD) && !skip_allocate_sids) {
+ ret = samldb_add_step(ac, samldb_allocate_sid);
+ if (ret != LDB_SUCCESS) return ret;
}
if (strcmp(ac->type, "user") == 0) {
@@ -897,6 +895,16 @@ static int samldb_objectclass_trigger(struct samldb_ctx *ac)
return LDB_ERR_OTHER;
}
+ /* Workstation and (read-only) DC objects do need objectclass "computer" */
+ if ((samdb_find_attribute(ldb, ac->msg,
+ "objectclass", "computer") == NULL) &&
+ (user_account_control &
+ (UF_SERVER_TRUST_ACCOUNT | UF_WORKSTATION_TRUST_ACCOUNT))) {
+ ldb_set_errstring(ldb,
+ "samldb: Requested account type does need objectclass 'computer'!");
+ return LDB_ERR_OBJECT_CLASS_VIOLATION;
+ }
+
account_type = ds_uf2atype(user_account_control);
if (account_type == 0) {
ldb_set_errstring(ldb, "samldb: Unrecognized account type!");
@@ -911,11 +919,20 @@ static int samldb_objectclass_trigger(struct samldb_ctx *ac)
el2 = ldb_msg_find_element(ac->msg, "sAMAccountType");
el2->flags = LDB_FLAG_MOD_REPLACE;
+ /* "isCriticalSystemObject" might be set */
if (user_account_control &
(UF_SERVER_TRUST_ACCOUNT | UF_PARTIAL_SECRETS_ACCOUNT)) {
- ret = samdb_msg_set_string(ldb, ac->msg, ac->msg,
- "isCriticalSystemObject",
- "TRUE");
+ ret = ldb_msg_add_string(ac->msg, "isCriticalSystemObject",
+ "TRUE");
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ el2 = ldb_msg_find_element(ac->msg,
+ "isCriticalSystemObject");
+ el2->flags = LDB_FLAG_MOD_REPLACE;
+ } else if (user_account_control & UF_WORKSTATION_TRUST_ACCOUNT) {
+ ret = ldb_msg_add_string(ac->msg, "isCriticalSystemObject",
+ "FALSE");
if (ret != LDB_SUCCESS) {
return ret;
}
@@ -927,6 +944,18 @@ static int samldb_objectclass_trigger(struct samldb_ctx *ac)
/* Step 1.4: "userAccountControl" -> "primaryGroupID" mapping */
if (!ldb_msg_find_element(ac->msg, "primaryGroupID")) {
uint32_t rid = ds_uf2prim_group_rid(user_account_control);
+
+ /*
+ * Older AD deployments don't know about the
+ * RODC group
+ */
+ if (rid == DOMAIN_RID_READONLY_DCS) {
+ ret = samldb_prim_group_tester(ac, rid);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+
ret = samdb_msg_add_uint(ldb, ac->msg, ac->msg,
"primaryGroupID", rid);
if (ret != LDB_SUCCESS) {
@@ -1009,26 +1038,14 @@ static int samldb_objectclass_trigger(struct samldb_ctx *ac)
* ac->msg contains the "add"/"modify" message
*/
-static int samldb_prim_group_set(struct samldb_ctx *ac)
+static int samldb_prim_group_tester(struct samldb_ctx *ac, uint32_t rid)
{
struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
- uint32_t rid;
struct dom_sid *sid;
struct ldb_result *res;
int ret;
const char *noattrs[] = { NULL };
- rid = ldb_msg_find_attr_as_uint(ac->msg, "primaryGroupID", (uint32_t) -1);
- if (rid == (uint32_t) -1) {
- /* we aren't affected of any primary group set */
- return LDB_SUCCESS;
-
- } else if (!ldb_request_get_control(ac->req, LDB_CONTROL_RELAX_OID)) {
- ldb_set_errstring(ldb,
- "The primary group isn't settable on add operations!");
- return LDB_ERR_UNWILLING_TO_PERFORM;
- }
-
sid = dom_sid_add_rid(ac, samdb_domain_sid(ldb), rid);
if (sid == NULL) {
return ldb_operr(ldb);
@@ -1054,6 +1071,25 @@ static int samldb_prim_group_set(struct samldb_ctx *ac)
return LDB_SUCCESS;
}
+static int samldb_prim_group_set(struct samldb_ctx *ac)
+{
+ struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
+ uint32_t rid;
+
+ rid = ldb_msg_find_attr_as_uint(ac->msg, "primaryGroupID", (uint32_t) -1);
+ if (rid == (uint32_t) -1) {
+ /* we aren't affected of any primary group set */
+ return LDB_SUCCESS;
+
+ } else if (!ldb_request_get_control(ac->req, LDB_CONTROL_RELAX_OID)) {
+ ldb_set_errstring(ldb,
+ "The primary group isn't settable on add operations!");
+ return LDB_ERR_UNWILLING_TO_PERFORM;
+ }
+
+ return samldb_prim_group_tester(ac, rid);
+}
+
static int samldb_prim_group_change(struct samldb_ctx *ac)
{
struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
@@ -1076,14 +1112,11 @@ static int samldb_prim_group_change(struct samldb_ctx *ac)
/* Fetch information from the existing object */
- ret = dsdb_module_search(ac->module, ac, &res, ac->msg->dn, LDB_SCOPE_BASE, attrs,
- DSDB_FLAG_NEXT_MODULE, ac->req, NULL);
+ ret = dsdb_module_search_dn(ac->module, ac, &res, ac->msg->dn, attrs,
+ DSDB_FLAG_NEXT_MODULE, ac->req);
if (ret != LDB_SUCCESS) {
return ret;
}
- if (res->count != 1) {
- return ldb_operr(ldb);
- }
/* Finds out the DN of the old primary group */
@@ -1219,13 +1252,22 @@ static int samldb_prim_group_trigger(struct samldb_ctx *ac)
return ret;
}
+
+/**
+ * This function is called on LDB modify operations. It performs some additions/
+ * replaces on the current LDB message when "userAccountControl" changes.
+ */
static int samldb_user_account_control_change(struct samldb_ctx *ac)
{
struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
- uint32_t user_account_control, account_type;
+ uint32_t user_account_control, old_user_account_control, account_type;
struct ldb_message_element *el;
struct ldb_message *tmp_msg;
int ret;
+ struct ldb_result *res;
+ const char *attrs[] = { "userAccountControl", "objectClass", NULL };
+ unsigned int i;
+ bool is_computer = false;
el = dsdb_get_single_valued_attr(ac->msg, "userAccountControl",
ac->req->operation);
@@ -1253,6 +1295,49 @@ static int samldb_user_account_control_change(struct samldb_ctx *ac)
return LDB_ERR_OTHER;
}
+ /* Fetch the old "userAccountControl" and "objectClass" */
+ ret = dsdb_module_search_dn(ac->module, ac, &res, ac->msg->dn, attrs,
+ DSDB_FLAG_NEXT_MODULE, ac->req);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ old_user_account_control = ldb_msg_find_attr_as_uint(res->msgs[0], "userAccountControl", 0);
+ if (old_user_account_control == 0) {
+ return ldb_operr(ldb);
+ }
+ el = ldb_msg_find_element(res->msgs[0], "objectClass");
+ if (el == NULL) {
+ return ldb_operr(ldb);
+ }
+
+ /* When we do not have objectclass "computer" we cannot switch to a (read-only) DC */
+ for (i = 0; i < el->num_values; i++) {
+ if (ldb_attr_cmp((char *)el->values[i].data, "computer") == 0) {
+ is_computer = true;
+ break;
+ }
+ }
+ if (!is_computer &&
+ (user_account_control & (UF_SERVER_TRUST_ACCOUNT | UF_PARTIAL_SECRETS_ACCOUNT))) {
+ ldb_set_errstring(ldb,
+ "samldb: Requested account type does need objectclass 'computer'!");
+ return LDB_ERR_UNWILLING_TO_PERFORM;
+ }
+
+ /*
+ * The functions "ds_uf2atype" and "ds_uf2prim_group_rid" are used as
+ * detectors for account type changes.
+ * So if the account type does change then we need to adjust the
+ * "sAMAccountType", the "isCriticalSystemObject" and the
+ * "primaryGroupID" attribute.
+ */
+ if ((ds_uf2atype(user_account_control)
+ == ds_uf2atype(old_user_account_control)) &&
+ (ds_uf2prim_group_rid(user_account_control)
+ == ds_uf2prim_group_rid(old_user_account_control))) {
+ return LDB_SUCCESS;
+ }
+
account_type = ds_uf2atype(user_account_control);
if (account_type == 0) {
ldb_set_errstring(ldb, "samldb: Unrecognized account type!");
@@ -1266,6 +1351,7 @@ static int samldb_user_account_control_change(struct samldb_ctx *ac)
el = ldb_msg_find_element(ac->msg, "sAMAccountType");
el->flags = LDB_FLAG_MOD_REPLACE;
+ /* "isCriticalSystemObject" might be set/changed */
if (user_account_control
& (UF_SERVER_TRUST_ACCOUNT | UF_PARTIAL_SECRETS_ACCOUNT)) {
ret = ldb_msg_add_string(ac->msg, "isCriticalSystemObject",
@@ -1276,10 +1362,28 @@ static int samldb_user_account_control_change(struct samldb_ctx *ac)
el = ldb_msg_find_element(ac->msg,
"isCriticalSystemObject");
el->flags = LDB_FLAG_MOD_REPLACE;
+ } else if (user_account_control & UF_WORKSTATION_TRUST_ACCOUNT) {
+ ret = ldb_msg_add_string(ac->msg, "isCriticalSystemObject",
+ "FALSE");
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ el = ldb_msg_find_element(ac->msg,
+ "isCriticalSystemObject");
+ el->flags = LDB_FLAG_MOD_REPLACE;
}
if (!ldb_msg_find_element(ac->msg, "primaryGroupID")) {
uint32_t rid = ds_uf2prim_group_rid(user_account_control);
+
+ /* Older AD deployments don't know about the RODC group */
+ if (rid == DOMAIN_RID_READONLY_DCS) {
+ ret = samldb_prim_group_tester(ac, rid);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+
ret = samdb_msg_add_uint(ldb, ac->msg, ac->msg,
"primaryGroupID", rid);
if (ret != LDB_SUCCESS) {
@@ -1977,7 +2081,7 @@ static int samldb_modify(struct ldb_module *module, struct ldb_request *req)
el = ldb_msg_find_element(ac->msg, "primaryGroupID");
if (el != NULL) {
- ret = samldb_prim_group_change(ac);
+ ret = samldb_prim_group_trigger(ac);
if (ret != LDB_SUCCESS) {
return ret;
}
diff --git a/source4/dsdb/samdb/ldb_modules/util.c b/source4/dsdb/samdb/ldb_modules/util.c
index 49939e2ff4..7dbf233703 100644
--- a/source4/dsdb/samdb/ldb_modules/util.c
+++ b/source4/dsdb/samdb/ldb_modules/util.c
@@ -109,39 +109,23 @@ int dsdb_module_search_dn(struct ldb_module *module,
return ret;
}
-/*
- search for attrs in the modules below
- */
-int dsdb_module_search(struct ldb_module *module,
+int dsdb_module_search_tree(struct ldb_module *module,
TALLOC_CTX *mem_ctx,
struct ldb_result **_res,
- struct ldb_dn *basedn, enum ldb_scope scope,
+ struct ldb_dn *basedn,
+ enum ldb_scope scope,
+ struct ldb_parse_tree *tree,
const char * const *attrs,
- int dsdb_flags,
- struct ldb_request *parent,
- const char *format, ...) _PRINTF_ATTRIBUTE(9, 10)
+ int dsdb_flags,
+ struct ldb_request *parent)
{
int ret;
struct ldb_request *req;
TALLOC_CTX *tmp_ctx;
struct ldb_result *res;
- va_list ap;
- char *expression;
tmp_ctx = talloc_new(mem_ctx);
- if (format) {
- va_start(ap, format);
- expression = talloc_vasprintf(tmp_ctx, format, ap);
- va_end(ap);
-
- if (!expression) {
- talloc_free(tmp_ctx);
- return ldb_oom(ldb_module_get_ctx(module));
- }
- } else {
- expression = NULL;
- }
res = talloc_zero(tmp_ctx, struct ldb_result);
if (!res) {
@@ -149,10 +133,10 @@ int dsdb_module_search(struct ldb_module *module,
return ldb_oom(ldb_module_get_ctx(module));
}
- ret = ldb_build_search_req(&req, ldb_module_get_ctx(module), tmp_ctx,
+ ret = ldb_build_search_req_ex(&req, ldb_module_get_ctx(module), tmp_ctx,
basedn,
scope,
- expression,
+ tree,
attrs,
NULL,
res,
@@ -196,6 +180,61 @@ int dsdb_module_search(struct ldb_module *module,
}
/*
+ search for attrs in the modules below
+ */
+int dsdb_module_search(struct ldb_module *module,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_result **_res,
+ struct ldb_dn *basedn, enum ldb_scope scope,
+ const char * const *attrs,
+ int dsdb_flags,
+ struct ldb_request *parent,
+ const char *format, ...) _PRINTF_ATTRIBUTE(9, 10)
+{
+ int ret;
+ TALLOC_CTX *tmp_ctx;
+ va_list ap;
+ char *expression;
+ struct ldb_parse_tree *tree;
+
+ tmp_ctx = talloc_new(mem_ctx);
+
+ if (format) {
+ va_start(ap, format);
+ expression = talloc_vasprintf(tmp_ctx, format, ap);
+ va_end(ap);
+
+ if (!expression) {
+ talloc_free(tmp_ctx);
+ return ldb_oom(ldb_module_get_ctx(module));
+ }
+ } else {
+ expression = NULL;
+ }
+
+ tree = ldb_parse_tree(tmp_ctx, expression);
+ if (tree == NULL) {
+ talloc_free(tmp_ctx);
+ ldb_set_errstring(ldb_module_get_ctx(module),
+ "Unable to parse search expression");
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = dsdb_module_search_tree(module,
+ mem_ctx,
+ _res,
+ basedn,
+ scope,
+ tree,
+ attrs,
+ dsdb_flags,
+ parent);
+
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+/*
find a DN given a GUID. This searches across all partitions
*/
int dsdb_module_dn_by_guid(struct ldb_module *module, TALLOC_CTX *mem_ctx,
diff --git a/source4/dsdb/samdb/ldb_modules/wscript_build b/source4/dsdb/samdb/ldb_modules/wscript_build
index 8ad893c551..eb9c664c71 100644
--- a/source4/dsdb/samdb/ldb_modules/wscript_build
+++ b/source4/dsdb/samdb/ldb_modules/wscript_build
@@ -390,3 +390,12 @@ bld.SAMBA_MODULE('ldb_simple_dn',
internal_module=False,
deps='talloc DSDB_MODULE_HELPERS'
)
+
+bld.SAMBA_MODULE('ldb_dirsync',
+ source='dirsync.c',
+ subsystem='ldb',
+ init_function='ldb_dirsync_module_init',
+ module_init_name='ldb_init_module',
+ internal_module=False,
+ deps='talloc events security samdb DSDB_MODULE_HELPERS DSDB_MODULE_HELPER_SCHEMA'
+ )