From cc1cf31e55b455f67d846ebe1fe60d305ae2ca11 Mon Sep 17 00:00:00 2001 From: Matthias Dieter Wallnöfer Date: Wed, 13 Apr 2011 09:14:44 +0200 Subject: s4:repl_meta_data LDB module - quiet a discard const ptr warning --- source4/dsdb/samdb/ldb_modules/repl_meta_data.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'source4/dsdb/samdb') diff --git a/source4/dsdb/samdb/ldb_modules/repl_meta_data.c b/source4/dsdb/samdb/ldb_modules/repl_meta_data.c index 646abeb771..90933c4c79 100644 --- a/source4/dsdb/samdb/ldb_modules/repl_meta_data.c +++ b/source4/dsdb/samdb/ldb_modules/repl_meta_data.c @@ -2488,7 +2488,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); -- cgit From bbf28703a47fc464a7d5dac25e6a4a6cf96fe3e9 Mon Sep 17 00:00:00 2001 From: Matthias Dieter Wallnöfer Date: Sun, 10 Apr 2011 20:47:18 +0200 Subject: s4:"ldb_connect" calls - proof for "!= LDB_SUCCESS" Reviewed-by: abartlet --- source4/dsdb/samdb/ldb_modules/proxy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'source4/dsdb/samdb') 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; } -- cgit From cdd802af8319e0b0744d8e727cef75526269ece2 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Tue, 3 May 2011 10:40:33 +1000 Subject: s4-messaging Rename messaging -> imessaging This avoid symbol and structure conflicts between Samba3 and Samba4, and chooses a less generic name. Andrew Bartlett --- source4/dsdb/samdb/ldb_modules/ridalloc.c | 6 +++--- source4/dsdb/samdb/ldb_modules/rootdse.c | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'source4/dsdb/samdb') 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..b718900345 100644 --- a/source4/dsdb/samdb/ldb_modules/rootdse.c +++ b/source4/dsdb/samdb/ldb_modules/rootdse.c @@ -1195,7 +1195,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 +1223,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, -- cgit From 2742ec0e34c06ded2885aa2607f1c1729a57b034 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Tue, 3 May 2011 12:16:16 +1000 Subject: Remove strlower_m() and strupper_m() from source4 and common code. This function is problematic because a string may expand in size when changed into upper or lower case. This will then push characters off the end of the string in the s3 implementation, or panic in the former s4 implementation. Andrew Bartlett --- source4/dsdb/samdb/ldb_modules/rootdse.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'source4/dsdb/samdb') diff --git a/source4/dsdb/samdb/ldb_modules/rootdse.c b/source4/dsdb/samdb/ldb_modules/rootdse.c index b718900345..7bc27b46b4 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), -- cgit From df83e9c15e93502e77cfa877d6bdf1beb9b4048f Mon Sep 17 00:00:00 2001 From: Matthieu Patou Date: Mon, 7 Feb 2011 09:58:17 +0300 Subject: s4: do not change the critical flag when it's on a dirsync control Signed-off-by: Andrew Tridgell --- source4/dsdb/samdb/ldb_modules/rootdse.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'source4/dsdb/samdb') diff --git a/source4/dsdb/samdb/ldb_modules/rootdse.c b/source4/dsdb/samdb/ldb_modules/rootdse.c index 7bc27b46b4..c584a11b2c 100644 --- a/source4/dsdb/samdb/ldb_modules/rootdse.c +++ b/source4/dsdb/samdb/ldb_modules/rootdse.c @@ -612,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; } } -- cgit From 1d0fc445fae4b908ac475d0beb5e1d8d14a3efcb Mon Sep 17 00:00:00 2001 From: Matthieu Patou Date: Sat, 16 Apr 2011 11:46:40 +0400 Subject: s4-dsdb: create flag for requesting ACL relax in case of DIRSYNC request Signed-off-by: Andrew Tridgell --- source4/dsdb/samdb/samdb.h | 1 + 1 file changed, 1 insertion(+) (limited to 'source4/dsdb/samdb') diff --git a/source4/dsdb/samdb/samdb.h b/source4/dsdb/samdb/samdb.h index 8efb5e0af2..96f44c356e 100644 --- a/source4/dsdb/samdb/samdb.h +++ b/source4/dsdb/samdb/samdb.h @@ -192,4 +192,5 @@ struct dsdb_fsmo_extended_op { struct GUID destination_dsa_guid; }; +#define DSDB_ACL_CHECKS_DIRSYNC_FLAG 0x1 #endif /* __SAMDB_H__ */ -- cgit From 37b1662a38259d59508faa1b6226406b02504a5b Mon Sep 17 00:00:00 2001 From: Matthieu Patou Date: Tue, 8 Mar 2011 01:02:32 +0300 Subject: s4-dsdb: relax a bit the checks on read acl when dirsync control is specified Signed-off-by: Andrew Tridgell --- source4/dsdb/samdb/ldb_modules/acl_read.c | 54 ++++++++++++++++++++++++------- 1 file changed, 42 insertions(+), 12 deletions(-) (limited to 'source4/dsdb/samdb') 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); } -- cgit From 7b4e1e78bef7ece31ee01ef3a1d7cfc3810ab27d Mon Sep 17 00:00:00 2001 From: Matthieu Patou Date: Sun, 20 Mar 2011 01:20:22 +0300 Subject: 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 --- source4/dsdb/samdb/ldb_modules/util.c | 87 +++++++++++++++++++++++++---------- 1 file changed, 63 insertions(+), 24 deletions(-) (limited to 'source4/dsdb/samdb') 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, @@ -195,6 +179,61 @@ int dsdb_module_search(struct ldb_module *module, return ret; } +/* + 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 */ -- cgit From fa400af18b7fdba2edd6c3b4c335dab64481545a Mon Sep 17 00:00:00 2001 From: Matthieu Patou Date: Thu, 13 Jan 2011 21:55:11 +0300 Subject: s4-dsdb: implementation of the dirsync control Signed-off-by: Andrew Tridgell --- source4/dsdb/samdb/ldb_modules/dirsync.c | 1359 ++++++++++++++++++++++++++ source4/dsdb/samdb/ldb_modules/samba_dsdb.c | 1 + source4/dsdb/samdb/ldb_modules/wscript_build | 9 + 3 files changed, 1369 insertions(+) create mode 100644 source4/dsdb/samdb/ldb_modules/dirsync.c (limited to 'source4/dsdb/samdb') 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 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 . +*/ + + +#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; jelements, 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; jop.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/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/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' + ) -- cgit From d34205add33c028dfd8ca1f8c364013b71f5b3a1 Mon Sep 17 00:00:00 2001 From: Matthias Dieter Wallnöfer Date: Sun, 10 Apr 2011 19:54:31 +0200 Subject: s4:ldb-samba/ldb_wrap.*-dsdb/samdb/samdb.c - handle LDB connection flags as unsigned The LDB API ("ldb_connect") prescribes that they should be "unsigned". Signed-off-by: Metze --- source4/dsdb/samdb/samdb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'source4/dsdb/samdb') diff --git a/source4/dsdb/samdb/samdb.c b/source4/dsdb/samdb/samdb.c index 29b454467c..cef0d1e54f 100644 --- a/source4/dsdb/samdb/samdb.c +++ b/source4/dsdb/samdb/samdb.c @@ -96,7 +96,7 @@ struct ldb_context *samdb_connect(TALLOC_CTX *mem_ctx, struct tevent_context *ev_ctx, struct loadparm_context *lp_ctx, struct auth_session_info *session_info, - int flags) + unsigned int flags) { struct ldb_context *ldb; struct dsdb_schema *schema; -- cgit From e3aa200a14e92f82c8233cad1b8062ffcc5a9a7e Mon Sep 17 00:00:00 2001 From: Matthieu Patou Date: Sat, 21 May 2011 11:56:45 +0400 Subject: s4:samldb LDB module - don't change the "primaryGroupId" on LDB modifications unless we are a computer/dc/rodc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Matthias Dieter Wallnöfer --- source4/dsdb/samdb/ldb_modules/samldb.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) (limited to 'source4/dsdb/samdb') diff --git a/source4/dsdb/samdb/ldb_modules/samldb.c b/source4/dsdb/samdb/ldb_modules/samldb.c index 21341850d9..216e14dc4b 100644 --- a/source4/dsdb/samdb/ldb_modules/samldb.c +++ b/source4/dsdb/samdb/ldb_modules/samldb.c @@ -1219,6 +1219,12 @@ static int samldb_prim_group_trigger(struct samldb_ctx *ac) return ret; } + +/** + * This function is called on a LDB modify. It performs some additions/changes + * on the current LDB message. Changes depend on the value of + * userAccountControl. + */ static int samldb_user_account_control_change(struct samldb_ctx *ac) { struct ldb_context *ldb = ldb_module_get_ctx(ac->module); @@ -1278,7 +1284,16 @@ static int samldb_user_account_control_change(struct samldb_ctx *ac) el->flags = LDB_FLAG_MOD_REPLACE; } - if (!ldb_msg_find_element(ac->msg, "primaryGroupID")) { + /* + * If the account has UF_SERVER_TRUST_ACCOUNT or + * UF_WORKSTATION_TRUST_ACCOUNT then change the group + * as it's either a workstation, a RODC, or a DC. + * + * If not it might be just a user that we are enabling + * and in this case we don't want to change its default group. + */ + if (user_account_control & (UF_SERVER_TRUST_ACCOUNT| UF_WORKSTATION_TRUST_ACCOUNT) && + !ldb_msg_find_element(ac->msg, "primaryGroupID")) { uint32_t rid = ds_uf2prim_group_rid(user_account_control); ret = samdb_msg_add_uint(ldb, ac->msg, ac->msg, "primaryGroupID", rid); -- cgit From b712c7273d2362f1f4ee7cd96f6f30c5035244d9 Mon Sep 17 00:00:00 2001 From: Matthias Dieter Wallnöfer Date: Mon, 23 May 2011 10:29:17 +0200 Subject: s4:samldb LDB module - fix the behaviour when changing the "userAccountControl" Ekacnet was not quite right yet but his patch made me think further. This primary group changing is only needed if the account type changes. With this patch we do one more search if the "userAccountControl" changes but we save us from doing these unneeded and wrong modify replace operations most of the time. Reviewed-by: abartlet --- source4/dsdb/samdb/ldb_modules/samldb.c | 45 +++++++++++++++++++++++---------- 1 file changed, 31 insertions(+), 14 deletions(-) (limited to 'source4/dsdb/samdb') diff --git a/source4/dsdb/samdb/ldb_modules/samldb.c b/source4/dsdb/samdb/ldb_modules/samldb.c index 216e14dc4b..aa9d6d30ce 100644 --- a/source4/dsdb/samdb/ldb_modules/samldb.c +++ b/source4/dsdb/samdb/ldb_modules/samldb.c @@ -1221,17 +1221,18 @@ static int samldb_prim_group_trigger(struct samldb_ctx *ac) /** - * This function is called on a LDB modify. It performs some additions/changes - * on the current LDB message. Changes depend on the value of - * userAccountControl. + * 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", NULL }; el = dsdb_get_single_valued_attr(ac->msg, "userAccountControl", ac->req->operation); @@ -1259,6 +1260,31 @@ static int samldb_user_account_control_change(struct samldb_ctx *ac) return LDB_ERR_OTHER; } + /* Fetch the old "userAccountControl" */ + 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); + } + + /* + * 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!"); @@ -1284,16 +1310,7 @@ static int samldb_user_account_control_change(struct samldb_ctx *ac) el->flags = LDB_FLAG_MOD_REPLACE; } - /* - * If the account has UF_SERVER_TRUST_ACCOUNT or - * UF_WORKSTATION_TRUST_ACCOUNT then change the group - * as it's either a workstation, a RODC, or a DC. - * - * If not it might be just a user that we are enabling - * and in this case we don't want to change its default group. - */ - if (user_account_control & (UF_SERVER_TRUST_ACCOUNT| UF_WORKSTATION_TRUST_ACCOUNT) && - !ldb_msg_find_element(ac->msg, "primaryGroupID")) { + if (!ldb_msg_find_element(ac->msg, "primaryGroupID")) { uint32_t rid = ds_uf2prim_group_rid(user_account_control); ret = samdb_msg_add_uint(ldb, ac->msg, ac->msg, "primaryGroupID", rid); -- cgit From 0c753e503c977c470aeb4fcce5b74f283ba2012f Mon Sep 17 00:00:00 2001 From: Matthias Dieter Wallnöfer Date: Mon, 23 May 2011 11:46:39 +0200 Subject: s4:samldb LDB module - fix "isCriticalSystemObject" behaviour Tests against Windows Server show that it gets set to "FALSE" (not deleted) if we change the account type to a domain member. Reviewed-by: abartlet --- source4/dsdb/samdb/ldb_modules/samldb.c | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) (limited to 'source4/dsdb/samdb') diff --git a/source4/dsdb/samdb/ldb_modules/samldb.c b/source4/dsdb/samdb/ldb_modules/samldb.c index aa9d6d30ce..bf91d29709 100644 --- a/source4/dsdb/samdb/ldb_modules/samldb.c +++ b/source4/dsdb/samdb/ldb_modules/samldb.c @@ -911,11 +911,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; } @@ -1298,6 +1307,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", @@ -1308,6 +1318,15 @@ 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")) { -- cgit From 2ad0100d5ba3e388ead950b0cc6dbf887f907625 Mon Sep 17 00:00:00 2001 From: Matthias Dieter Wallnöfer Date: Mon, 23 May 2011 12:51:06 +0200 Subject: s4:samldb LDB modules - only objectClass "computer" is allowed to embed all types of account Reviewed-by: abartlet --- source4/dsdb/samdb/ldb_modules/samldb.c | 36 ++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) (limited to 'source4/dsdb/samdb') diff --git a/source4/dsdb/samdb/ldb_modules/samldb.c b/source4/dsdb/samdb/ldb_modules/samldb.c index bf91d29709..fa0c4f6317 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 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 @@ -897,6 +897,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!"); @@ -1241,7 +1251,9 @@ static int samldb_user_account_control_change(struct samldb_ctx *ac) struct ldb_message *tmp_msg; int ret; struct ldb_result *res; - const char *attrs[] = { "userAccountControl", NULL }; + 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); @@ -1269,7 +1281,7 @@ static int samldb_user_account_control_change(struct samldb_ctx *ac) return LDB_ERR_OTHER; } - /* Fetch the old "userAccountControl" */ + /* 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) { @@ -1279,6 +1291,24 @@ static int samldb_user_account_control_change(struct samldb_ctx *ac) 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 -- cgit From 779d882aca4fb0486bea0109c3630e1d7abef840 Mon Sep 17 00:00:00 2001 From: Matthias Dieter Wallnöfer Date: Mon, 23 May 2011 12:59:58 +0200 Subject: s4:samldb LDB module - convert a "dsdb_module_search" into "dsdb_module_search_dn" It saves us from checking the number of returned entries. Reviewed-by: abartlet --- source4/dsdb/samdb/ldb_modules/samldb.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'source4/dsdb/samdb') diff --git a/source4/dsdb/samdb/ldb_modules/samldb.c b/source4/dsdb/samdb/ldb_modules/samldb.c index fa0c4f6317..467558d32d 100644 --- a/source4/dsdb/samdb/ldb_modules/samldb.c +++ b/source4/dsdb/samdb/ldb_modules/samldb.c @@ -1095,14 +1095,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 */ -- cgit From 08f5ed8b4f21f1c52ce7a5de8c692df20d21f571 Mon Sep 17 00:00:00 2001 From: Matthias Dieter Wallnöfer Date: Tue, 24 May 2011 21:21:19 +0200 Subject: s4:samldb LDB module - better to call "samldb_prim_group_trigger" "samldb_prim_group_trigger" which as a wrapper calls "samldb_prim_group_change" for a LDB modify operation. Reviewed-by: abartlet --- source4/dsdb/samdb/ldb_modules/samldb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'source4/dsdb/samdb') diff --git a/source4/dsdb/samdb/ldb_modules/samldb.c b/source4/dsdb/samdb/ldb_modules/samldb.c index 467558d32d..3080d8c9b3 100644 --- a/source4/dsdb/samdb/ldb_modules/samldb.c +++ b/source4/dsdb/samdb/ldb_modules/samldb.c @@ -2055,7 +2055,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; } -- cgit From ff47927fb960b9a6c9a1ca7236dc4562c5a68461 Mon Sep 17 00:00:00 2001 From: Matthias Dieter Wallnöfer Date: Tue, 24 May 2011 22:05:46 +0200 Subject: s4:samldb LDB module - check if the RODC group exists if creating an RODC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Older AD deployments simply don't have it and hence there is no RODC support. Reviewed-by: abartlet Autobuild-User: Matthias Dieter Wallnöfer Autobuild-Date: Wed May 25 10:26:37 CEST 2011 on sn-devel-104 --- source4/dsdb/samdb/ldb_modules/samldb.c | 56 +++++++++++++++++++++++++-------- 1 file changed, 43 insertions(+), 13 deletions(-) (limited to 'source4/dsdb/samdb') diff --git a/source4/dsdb/samdb/ldb_modules/samldb.c b/source4/dsdb/samdb/ldb_modules/samldb.c index 3080d8c9b3..07c9cdd312 100644 --- a/source4/dsdb/samdb/ldb_modules/samldb.c +++ b/source4/dsdb/samdb/ldb_modules/samldb.c @@ -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) * @@ -946,6 +948,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) { @@ -1028,26 +1042,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); @@ -1073,6 +1075,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); @@ -1358,6 +1379,15 @@ static int samldb_user_account_control_change(struct samldb_ctx *ac) 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) { -- cgit From c091a92be51e8c14bf0b51ab83319fbcb704c91f Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Thu, 2 Jun 2011 15:43:40 +1000 Subject: s4-param Remove 'sam database' parameter This now just relies on the private dir parameter, which remains. Andrew Bartlett --- source4/dsdb/samdb/samdb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'source4/dsdb/samdb') diff --git a/source4/dsdb/samdb/samdb.c b/source4/dsdb/samdb/samdb.c index cef0d1e54f..d761107b15 100644 --- a/source4/dsdb/samdb/samdb.c +++ b/source4/dsdb/samdb/samdb.c @@ -104,7 +104,7 @@ struct ldb_context *samdb_connect(TALLOC_CTX *mem_ctx, struct cli_credentials *credentials; int ret; - url = lpcfg_sam_url(lp_ctx); + url = "sam.ldb"; credentials = samdb_credentials(lp_ctx); ldb = ldb_wrap_find(url, ev_ctx, lp_ctx, session_info, credentials, flags); -- cgit From a18efb1490cebd92205973c50ce1582b091a7676 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Thu, 2 Jun 2011 18:56:10 +1000 Subject: s4-param Remove 'sid generator' This was only used by the Fedora DS backend for Samba4. We agreed to no longer support external LDAP backends. Andrew Bartlett --- source4/dsdb/samdb/ldb_modules/samldb.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) (limited to 'source4/dsdb/samdb') diff --git a/source4/dsdb/samdb/ldb_modules/samldb.c b/source4/dsdb/samdb/ldb_modules/samldb.c index 07c9cdd312..6533d1006b 100644 --- a/source4/dsdb/samdb/ldb_modules/samldb.c +++ b/source4/dsdb/samdb/ldb_modules/samldb.c @@ -803,10 +803,9 @@ static int samldb_prim_group_tester(struct samldb_ctx *ac, uint32_t rid); 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; @@ -832,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) { -- cgit From 5d7ba305490b5047b4d404353e95c217c8ef7d10 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Tue, 7 Jun 2011 10:44:48 +1000 Subject: s4-dsdb: cope with missing backlinks in rpmd handling if backlinks have not propogated correctly in a previous replication this allows us to recover --- source4/dsdb/samdb/ldb_modules/repl_meta_data.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'source4/dsdb/samdb') diff --git a/source4/dsdb/samdb/ldb_modules/repl_meta_data.c b/source4/dsdb/samdb/ldb_modules/repl_meta_data.c index 90933c4c79..04311a4a98 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), -- cgit From 56d09d5904e036ae9cf483b7be202956aae998b3 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Fri, 10 Jun 2011 10:10:04 +1000 Subject: s4-drs: ensure we add a RMD_ADDTIME when upgrading a linked attribute if the link was a w2k style, and we are upgrading it, then set the RMD_ADDTIME to the current time --- source4/dsdb/samdb/ldb_modules/repl_meta_data.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'source4/dsdb/samdb') diff --git a/source4/dsdb/samdb/ldb_modules/repl_meta_data.c b/source4/dsdb/samdb/ldb_modules/repl_meta_data.c index 04311a4a98..9d2e5e2ac3 100644 --- a/source4/dsdb/samdb/ldb_modules/repl_meta_data.c +++ b/source4/dsdb/samdb/ldb_modules/repl_meta_data.c @@ -1643,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; } -- cgit From d9ee7aebcb26c6115e0caeacb90f3f916a5af600 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Wed, 22 Jun 2011 17:05:08 +1000 Subject: s4-dsdb: catch duplicate matches in extended_dn_in When searching using extended DNs, if there are multiple matches then return an object not found error. This is needed for the case of a duplicate objectSid, which happens for S-1-5-17 Pair-Programmed-With: Andrew Bartlett --- source4/dsdb/samdb/ldb_modules/extended_dn_in.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'source4/dsdb/samdb') diff --git a/source4/dsdb/samdb/ldb_modules/extended_dn_in.c b/source4/dsdb/samdb/ldb_modules/extended_dn_in.c index 3e2004d6f3..e2bb0de054 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; -- cgit From c42aeb7872c89983ea274d72b7ef8d9c7a59bc08 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Wed, 22 Jun 2011 17:07:39 +1000 Subject: s4-dsdb: prioritise GUID in extended_dn_in if we search with a base DN that has both a GUID and a SID, then use the GUID first. This matters for the S-1-5-17 SID. Pair-Programmed-With: Andrew Bartlett --- source4/dsdb/samdb/ldb_modules/extended_dn_in.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) (limited to 'source4/dsdb/samdb') diff --git a/source4/dsdb/samdb/ldb_modules/extended_dn_in.c b/source4/dsdb/samdb/ldb_modules/extended_dn_in.c index e2bb0de054..9a70d9a3db 100644 --- a/source4/dsdb/samdb/ldb_modules/extended_dn_in.c +++ b/source4/dsdb/samdb/ldb_modules/extended_dn_in.c @@ -315,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; -- cgit From a353b49047a54461a1b4fd3c5f232adcea5fbeaf Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Wed, 22 Jun 2011 18:14:14 +1000 Subject: s4-dsdb: bypass validation when relax set this allows dbcheck to fix bad attributes Autobuild-User: Andrew Tridgell Autobuild-Date: Wed Jun 22 12:27:06 CEST 2011 on sn-devel-104 --- source4/dsdb/samdb/ldb_modules/objectclass_attrs.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'source4/dsdb/samdb') 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)); -- cgit