The branch, master has been updated via 7615b25 samba-tool dbcheck: Correctly remove deleted DNs in dbcheck via f2afdb6 dsdb: Include MS-ADTS doc references on deleted object contstraints via a9e565a dsdb tests: Add member/memberOf checking to delete_objects testing via 0162be3 dsdb: Improve DRS deleted link source/target handing in repl_meta_data via 32955a1 dsdb: Ensure we always force deleted objects back under the deleted objects DN via a796cad dsdb/repl_meta_data: split out replmd_deletion_state() via d3aad89 dsdb: Prune deleted objects of links and extra attributes of replicated deletes from 8f8e843 s3:winbind: add a warning DEBUG message when skipping a sid from the mapped GID list
http://gitweb.samba.org/?p=samba.git;a=shortlog;h=master - Log ----------------------------------------------------------------- commit 7615b2549d9549683978cb3e85b926e2ba63e294 Author: Andrew Bartlett <abart...@samba.org> Date: Sun Apr 14 13:32:49 2013 +1000 samba-tool dbcheck: Correctly remove deleted DNs in dbcheck The previous pattern never matched, as it was a typo. Andrew Bartlett Signed-off-by: Andrew Bartlett <abart...@samba.org> Reviewed-by: Stefan Metzmacher <me...@samba.org> Autobuild-User(master): Stefan Metzmacher <me...@samba.org> Autobuild-Date(master): Tue Jul 30 12:55:00 CEST 2013 on sn-devel-104 commit f2afdb61698c37389be286f9443471d4aeba49b8 Author: Andrew Bartlett <abart...@samba.org> Date: Tue Jun 4 20:22:31 2013 +1000 dsdb: Include MS-ADTS doc references on deleted object contstraints Signed-off-by: Andrew Bartlett <abart...@samba.org> Reviewed-by: Stefan Metzmacher <me...@samba.org> commit a9e565a5a4478f7b923f35311e170de2044ff848 Author: Andrew Bartlett <abart...@samba.org> Date: Mon Jun 3 17:51:41 2013 +1000 dsdb tests: Add member/memberOf checking to delete_objects testing Pair-Programmed-With: Stefan Metzmacher <me...@samba.org> Signed-off-by: Andrew Bartlett <abart...@samba.org> Signed-off-by: Stefan Metzmacher <me...@samba.org> commit 0162be32ab4f9716a4300d1f1a0caae8b0133f7c Author: Andrew Bartlett <abart...@samba.org> Date: Tue Jun 4 19:57:06 2013 +1000 dsdb: Improve DRS deleted link source/target handing in repl_meta_data We now correctly ignore the link updates if the source or target is deleted locally. This fixes the long-standing failure in the vampire_dc dbcheck test. Pair-Programmed-With: Stefan Metzmacher <me...@samba.org> Andrew Bartlett Signed-off-by: Andrew Bartlett <abart...@samba.org> Signed-off-by: Stefan Metzmacher <me...@samba.org> commit 32955a1dec3a97ab4550869dbeb5034247f3b1bc Author: Andrew Bartlett <abart...@samba.org> Date: Mon Jun 17 22:37:20 2013 +1000 dsdb: Ensure we always force deleted objects back under the deleted objects DN Signed-off-by: Andrew Bartlett <abart...@samba.org> Reviewed-by: Stefan Metzmacher <me...@samba.org> commit a796cad90f1028ccc54a3539e34dc0728b990a96 Author: Stefan Metzmacher <me...@samba.org> Date: Wed Jun 5 09:35:42 2013 +0200 dsdb/repl_meta_data: split out replmd_deletion_state() Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit d3aad891c5759f66bd891cb47866d908a0562a8a Author: Andrew Bartlett <abart...@samba.org> Date: Fri May 31 20:01:17 2013 +1000 dsdb: Prune deleted objects of links and extra attributes of replicated deletes When an object is deleted, the links to be removed are not propogated, you have to watch out for them manually! We do this by calling back into the originating update delete code (ie what is called if you ldb_delete() locally) so that any extra attribute found locally and not on the remote server becomes removed remotely too. We currently do the same with links, but that isn't strictly correct, but for now our getNCChanges server code filters these out, so only the usn is bumped. Andrew Bartlett Signed-off-by: Andrew Bartlett <abart...@samba.org> Reviewed-by: Stefan Metzmacher <me...@samba.org> ----------------------------------------------------------------------- Summary of changes: python/samba/dbchecker.py | 2 +- selftest/knownfail | 1 - source4/dsdb/samdb/ldb_modules/repl_meta_data.c | 568 ++++++++++++++++++----- source4/torture/drs/python/delete_object.py | 278 ++++++++++- 4 files changed, 698 insertions(+), 151 deletions(-) Changeset truncated at 500 lines: diff --git a/python/samba/dbchecker.py b/python/samba/dbchecker.py index e88f876..8b175c2 100644 --- a/python/samba/dbchecker.py +++ b/python/samba/dbchecker.py @@ -271,7 +271,7 @@ newSuperior: %s""" % (str(from_dn), str(to_rdn), str(to_base))) """handle a missing target DN (both GUID and DN string form are missing)""" # check if its a backlink linkID = self.samdb_schema.get_linkId_from_lDAPDisplayName(attrname) - if (linkID & 1 == 0) and str(dsdb_dn).find('DEL\\0A') == -1: + if (linkID & 1 == 0) and str(dsdb_dn).find('\\0ADEL') == -1: self.report("Not removing dangling forward link") return self.err_deleted_dn(dn, attrname, val, dsdb_dn, dsdb_dn) diff --git a/selftest/knownfail b/selftest/knownfail index 4fe96f3..0c501fa 100644 --- a/selftest/knownfail +++ b/selftest/knownfail @@ -176,7 +176,6 @@ ^samba4.ntvfs.cifs.krb5.base.createx_access.createx_access\(.*\)$ ^samba4.rpc.lsa.forest.trust #Not fully provided by Samba4 ^samba4.blackbox.kinit\(.*\).kinit with user password for expired password\(.*\) # We need to work out why this fails only during the pw change -^samba4.blackbox.dbcheck\(vampire_dc\).dbcheck\(vampire_dc:local\) # Due to replicating with --domain-critical-only we fail dbcheck on this database ^samba4.blackbox.upgradeprovision.alpha13.ldapcmp_sd\(none\) # Due to something rewriting the NT ACL on DNS objects ^samba4.blackbox.upgradeprovision.alpha13.ldapcmp_full_sd\(none\) # Due to something rewriting the NT ACL on DNS objects ^samba4.blackbox.upgradeprovision.release-4-0-0.ldapcmp_sd\(none\) # Due to something rewriting the NT ACL on DNS objects diff --git a/source4/dsdb/samdb/ldb_modules/repl_meta_data.c b/source4/dsdb/samdb/ldb_modules/repl_meta_data.c index b6ff7ff..c8cdfec 100644 --- a/source4/dsdb/samdb/ldb_modules/repl_meta_data.c +++ b/source4/dsdb/samdb/ldb_modules/repl_meta_data.c @@ -2,10 +2,10 @@ ldb database library Copyright (C) Simo Sorce 2004-2008 - Copyright (C) Andrew Bartlett <abart...@samba.org> 2005 - Copyright (C) Andrew Tridgell 2005 + Copyright (C) Andrew Bartlett <abart...@samba.org> 2005-2013 + Copyright (C) Andrew Tridgell 2005-2009 Copyright (C) Stefan Metzmacher <me...@samba.org> 2007 - Copyright (C) Matthieu Patou <m...@samba.org> 2010 + Copyright (C) Matthieu Patou <m...@samba.org> 2010-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 @@ -92,9 +92,12 @@ struct replmd_replicated_request { uint64_t seq_num; bool is_urgent; + + bool isDeleted; }; static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar); +static int replmd_delete_internals(struct ldb_module *module, struct ldb_request *req, bool re_delete); enum urgent_situation { REPL_URGENT_ON_CREATE = 1, @@ -102,6 +105,70 @@ enum urgent_situation { REPL_URGENT_ON_DELETE = 4 }; +enum deletion_state { + OBJECT_NOT_DELETED=1, + OBJECT_DELETED=2, + OBJECT_RECYCLED=3, + OBJECT_TOMBSTONE=4, + OBJECT_REMOVED=5 +}; + +static void replmd_deletion_state(struct ldb_module *module, + const struct ldb_message *msg, + enum deletion_state *current_state, + enum deletion_state *next_state) +{ + int ret; + bool enabled = false; + + if (msg == NULL) { + *current_state = OBJECT_REMOVED; + if (next_state != NULL) { + *next_state = OBJECT_REMOVED; + } + return; + } + + ret = dsdb_recyclebin_enabled(module, &enabled); + if (ret != LDB_SUCCESS) { + enabled = false; + } + + if (ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")) { + if (!enabled) { + *current_state = OBJECT_TOMBSTONE; + if (next_state != NULL) { + *next_state = OBJECT_REMOVED; + } + return; + } + + if (ldb_msg_check_string_attribute(msg, "isRecycled", "TRUE")) { + *current_state = OBJECT_RECYCLED; + if (next_state != NULL) { + *next_state = OBJECT_REMOVED; + } + return; + } + + *current_state = OBJECT_DELETED; + if (next_state != NULL) { + *next_state = OBJECT_RECYCLED; + } + return; + } + + *current_state = OBJECT_NOT_DELETED; + if (next_state == NULL) { + return; + } + + if (enabled) { + *next_state = OBJECT_DELETED; + } else { + *next_state = OBJECT_TOMBSTONE; + } +} static const struct { const char *update_name; @@ -154,7 +221,7 @@ static bool replmd_check_urgent_attribute(const struct ldb_message_element *el) } -static int replmd_replicated_apply_next(struct replmd_replicated_request *ar); +static int replmd_replicated_apply_isDeleted(struct replmd_replicated_request *ar); /* initialise the module @@ -456,10 +523,7 @@ static int replmd_op_callback(struct ldb_request *req, struct ldb_reply *ares) } if (ac->apply_mode) { - talloc_free(ares); - ac->index_current++; - - ret = replmd_replicated_apply_next(ac); + ret = replmd_replicated_apply_isDeleted(ac); if (ret != LDB_SUCCESS) { return ldb_module_done(ac->req, NULL, NULL, ret); } @@ -2735,8 +2799,11 @@ static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *are } /* - remove links from objects that point at this object when an object - is deleted + * remove links from objects that point at this object when an object + * is deleted. We remove it from the NEXT module per MS-DRSR 5.160 + * RemoveObj which states that link removal due to the object being + * deleted is NOT an originating update - they just go away! + * */ static int replmd_delete_remove_link(struct ldb_module *module, const struct dsdb_schema *schema, @@ -2817,8 +2884,13 @@ static int replmd_delete_remove_link(struct ldb_module *module, This also handles the mapping of delete to a rename operation to allow deletes to be replicated. + + It also handles the incoming deleted objects, to ensure they are + fully deleted here. In that case re_delete is true, and we do not + use this as a signal to change the deleted state, just reinforce it. + */ -static int replmd_delete(struct ldb_module *module, struct ldb_request *req) +static int replmd_delete_internals(struct ldb_module *module, struct ldb_request *req, bool re_delete) { int ret = LDB_ERR_OTHER; bool retb, disallow_move_on_delete; @@ -2844,10 +2916,7 @@ static int replmd_delete(struct ldb_module *module, struct ldb_request *req) "trustType", "trustAttributes", "userAccountControl", "uSNChanged", "uSNCreated", "whenCreated", "whenChanged", NULL}; unsigned int i, el_count = 0; - enum deletion_state { OBJECT_NOT_DELETED=1, OBJECT_DELETED=2, OBJECT_RECYCLED=3, - OBJECT_TOMBSTONE=4, OBJECT_REMOVED=5 }; enum deletion_state deletion_state, next_deletion_state; - bool enabled; if (ldb_dn_is_special(req->op.del.dn)) { return ldb_next_request(module, req); @@ -2861,6 +2930,7 @@ static int replmd_delete(struct ldb_module *module, struct ldb_request *req) schema = dsdb_get_schema(ldb, tmp_ctx); if (!schema) { + talloc_free(tmp_ctx); return LDB_ERR_OPERATIONS_ERROR; } @@ -2874,36 +2944,24 @@ static int replmd_delete(struct ldb_module *module, struct ldb_request *req) DSDB_SEARCH_REVEAL_INTERNALS | DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT, req); if (ret != LDB_SUCCESS) { + ldb_asprintf_errstring(ldb_module_get_ctx(module), + "repmd_delete: Failed to %s %s, because we failed to find it: %s", + re_delete ? "re-delete" : "delete", + ldb_dn_get_linearized(old_dn), + ldb_errstring(ldb_module_get_ctx(module))); talloc_free(tmp_ctx); return ret; } old_msg = res->msgs[0]; + replmd_deletion_state(module, old_msg, + &deletion_state, + &next_deletion_state); - ret = dsdb_recyclebin_enabled(module, &enabled); - if (ret != LDB_SUCCESS) { - talloc_free(tmp_ctx); - return ret; - } - - if (ldb_msg_check_string_attribute(old_msg, "isDeleted", "TRUE")) { - if (!enabled) { - deletion_state = OBJECT_TOMBSTONE; - next_deletion_state = OBJECT_REMOVED; - } else if (ldb_msg_check_string_attribute(old_msg, "isRecycled", "TRUE")) { - deletion_state = OBJECT_RECYCLED; - next_deletion_state = OBJECT_REMOVED; - } else { - deletion_state = OBJECT_DELETED; - next_deletion_state = OBJECT_RECYCLED; - } - } else { - deletion_state = OBJECT_NOT_DELETED; - if (enabled) { - next_deletion_state = OBJECT_DELETED; - } else { - next_deletion_state = OBJECT_TOMBSTONE; - } + /* This supports us noticing an incoming isDeleted and acting on it */ + if (re_delete) { + SMB_ASSERT(deletion_state > OBJECT_NOT_DELETED); + next_deletion_state = deletion_state; } if (next_deletion_state == OBJECT_REMOVED) { @@ -2936,45 +2994,57 @@ static int replmd_delete(struct ldb_module *module, struct ldb_request *req) msg->dn = old_dn; - if (deletion_state == OBJECT_NOT_DELETED){ - /* consider the SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE flag */ - disallow_move_on_delete = - (ldb_msg_find_attr_as_int(old_msg, "systemFlags", 0) - & SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE); + /* consider the SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE flag */ + disallow_move_on_delete = + (ldb_msg_find_attr_as_int(old_msg, "systemFlags", 0) + & SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE); - /* work out where we will be renaming this object to */ - if (!disallow_move_on_delete) { - ret = dsdb_get_deleted_objects_dn(ldb, tmp_ctx, old_dn, - &new_dn); - if (ret != LDB_SUCCESS) { - /* this is probably an attempted delete on a partition - * that doesn't allow delete operations, such as the - * schema partition */ - ldb_asprintf_errstring(ldb, "No Deleted Objects container for DN %s", - ldb_dn_get_linearized(old_dn)); - talloc_free(tmp_ctx); - return LDB_ERR_UNWILLING_TO_PERFORM; - } - } else { + /* work out where we will be renaming this object to */ + if (!disallow_move_on_delete) { + ret = dsdb_get_deleted_objects_dn(ldb, tmp_ctx, old_dn, + &new_dn); + /* + * Deleted Objects itself appears to be deleted, but + * should also not be moved, and we should not move + * objects if we can't find the deleted objects DN + */ + if (re_delete && (ret != LDB_SUCCESS || ldb_dn_compare(old_dn, new_dn) == 0)) { new_dn = ldb_dn_get_parent(tmp_ctx, old_dn); if (new_dn == NULL) { ldb_module_oom(module); talloc_free(tmp_ctx); return LDB_ERR_OPERATIONS_ERROR; } + } else if (ret != LDB_SUCCESS) { + /* this is probably an attempted delete on a partition + * that doesn't allow delete operations, such as the + * schema partition */ + ldb_asprintf_errstring(ldb, "No Deleted Objects container for DN %s", + ldb_dn_get_linearized(old_dn)); + talloc_free(tmp_ctx); + return LDB_ERR_UNWILLING_TO_PERFORM; } + } else { + new_dn = ldb_dn_get_parent(tmp_ctx, old_dn); + if (new_dn == NULL) { + ldb_module_oom(module); + talloc_free(tmp_ctx); + return LDB_ERR_OPERATIONS_ERROR; + } + } + if (deletion_state == OBJECT_NOT_DELETED) { /* get the objects GUID from the search we just did */ guid = samdb_result_guid(old_msg, "objectGUID"); /* Add a formatted child */ retb = ldb_dn_add_child_fmt(new_dn, "%s=%s\\0ADEL:%s", - rdn_name, - ldb_dn_escape_value(tmp_ctx, *rdn_value), - GUID_string(tmp_ctx, &guid)); + rdn_name, + ldb_dn_escape_value(tmp_ctx, *rdn_value), + GUID_string(tmp_ctx, &guid)); if (!retb) { DEBUG(0,(__location__ ": Unable to add a formatted child to dn: %s", - ldb_dn_get_linearized(new_dn))); + ldb_dn_get_linearized(new_dn))); talloc_free(tmp_ctx); return LDB_ERR_OPERATIONS_ERROR; } @@ -2987,6 +3057,30 @@ static int replmd_delete(struct ldb_module *module, struct ldb_request *req) return ret; } msg->elements[el_count++].flags = LDB_FLAG_MOD_REPLACE; + } else { + /* + * No matter what has happened with other renames etc, try again to + * get this to be under the deleted DN. See MS-DRSR 5.160 RemoveObj + */ + + struct ldb_dn *rdn = ldb_dn_copy(tmp_ctx, old_dn); + retb = ldb_dn_remove_base_components(rdn, ldb_dn_get_comp_num(rdn) - 1); + if (!retb) { + DEBUG(0,(__location__ ": Unable to add a prepare rdn of %s", + ldb_dn_get_linearized(rdn))); + talloc_free(tmp_ctx); + return LDB_ERR_OPERATIONS_ERROR; + } + SMB_ASSERT(ldb_dn_get_comp_num(rdn) == 1); + + retb = ldb_dn_add_child(new_dn, rdn); + if (!retb) { + DEBUG(0,(__location__ ": Unable to add rdn %s to base dn: %s", + ldb_dn_get_linearized(rdn), + ldb_dn_get_linearized(new_dn))); + talloc_free(tmp_ctx); + return LDB_ERR_OPERATIONS_ERROR; + } } /* @@ -3006,63 +3100,64 @@ static int replmd_delete(struct ldb_module *module, struct ldb_request *req) see MS-ADTS "Tombstone Requirements" section 3.1.1.5.5.1.1 */ - /* we need the storage form of the parent GUID */ - ret = dsdb_module_search_dn(module, tmp_ctx, &parent_res, - ldb_dn_get_parent(tmp_ctx, old_dn), NULL, - DSDB_FLAG_NEXT_MODULE | - DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT | - DSDB_SEARCH_REVEAL_INTERNALS| - DSDB_SEARCH_SHOW_RECYCLED, req); - if (ret != LDB_SUCCESS) { - talloc_free(tmp_ctx); - return ret; - } - - if (deletion_state == OBJECT_NOT_DELETED){ - ret = ldb_msg_add_steal_string(msg, "lastKnownParent", - ldb_dn_get_extended_linearized(tmp_ctx, parent_res->msgs[0]->dn, 1)); + if (deletion_state == OBJECT_NOT_DELETED) { + /* we need the storage form of the parent GUID */ + ret = dsdb_module_search_dn(module, tmp_ctx, &parent_res, + ldb_dn_get_parent(tmp_ctx, old_dn), NULL, + DSDB_FLAG_NEXT_MODULE | + DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT | + DSDB_SEARCH_REVEAL_INTERNALS| + DSDB_SEARCH_SHOW_RECYCLED, req); if (ret != LDB_SUCCESS) { - DEBUG(0,(__location__ ": Failed to add lastKnownParent string to the msg\n")); - ldb_module_oom(module); + ldb_asprintf_errstring(ldb_module_get_ctx(module), + "repmd_delete: Failed to %s %s, because we failed to find it's parent (%s): %s", + re_delete ? "re-delete" : "delete", + ldb_dn_get_linearized(old_dn), + ldb_dn_get_linearized(ldb_dn_get_parent(tmp_ctx, old_dn)), + ldb_errstring(ldb_module_get_ctx(module))); talloc_free(tmp_ctx); return ret; } - msg->elements[el_count++].flags = LDB_FLAG_MOD_REPLACE; - } - switch (next_deletion_state){ - - case OBJECT_DELETED: - - ret = ldb_msg_add_value(msg, "msDS-LastKnownRDN", rdn_value, NULL); + ret = ldb_msg_add_steal_string(msg, "lastKnownParent", + ldb_dn_get_extended_linearized(tmp_ctx, parent_res->msgs[0]->dn, 1)); if (ret != LDB_SUCCESS) { - DEBUG(0,(__location__ ": Failed to add msDS-LastKnownRDN string to the msg\n")); + DEBUG(0,(__location__ ": Failed to add lastKnownParent string to the msg\n")); ldb_module_oom(module); talloc_free(tmp_ctx); return ret; } - msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD; - - ret = ldb_msg_add_empty(msg, "objectCategory", LDB_FLAG_MOD_REPLACE, NULL); - if (ret != LDB_SUCCESS) { - talloc_free(tmp_ctx); - ldb_module_oom(module); - return ret; - } + msg->elements[el_count++].flags = LDB_FLAG_MOD_REPLACE; - ret = ldb_msg_add_empty(msg, "sAMAccountType", LDB_FLAG_MOD_REPLACE, NULL); - if (ret != LDB_SUCCESS) { - talloc_free(tmp_ctx); - ldb_module_oom(module); - return ret; + if (next_deletion_state == OBJECT_DELETED) { + ret = ldb_msg_add_value(msg, "msDS-LastKnownRDN", rdn_value, NULL); + if (ret != LDB_SUCCESS) { + DEBUG(0,(__location__ ": Failed to add msDS-LastKnownRDN string to the msg\n")); + ldb_module_oom(module); + talloc_free(tmp_ctx); + return ret; + } + msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD; } + } - break; + switch (next_deletion_state) { case OBJECT_RECYCLED: case OBJECT_TOMBSTONE: /* + * MS-ADTS 3.1.1.5.5.1.1 Tombstone Requirements + * describes what must be removed from a tombstone + * object + * + * MS-ADTS 3.1.1.5.5.1.3 Recycled-Object Requirements + * describes what must be removed from a recycled + * object + * + */ + + /* * we also mark it as recycled, meaning this object can't be * recovered (we are stripping its attributes). * This is done only if we have this schema object of course ... @@ -3078,7 +3173,7 @@ static int replmd_delete(struct ldb_module *module, struct ldb_request *req) talloc_free(tmp_ctx); return ret; } - msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD; + msg->elements[el_count++].flags = LDB_FLAG_MOD_REPLACE; } /* work out which of the old attributes we will be removing */ @@ -3124,6 +3219,41 @@ static int replmd_delete(struct ldb_module *module, struct ldb_request *req) return ret; } } + + /* Duplicate with the below - we remove the + * samAccountType as an originating update, in case it + * somehow came back. The objectCategory will have + * gone in the above */ + ret = ldb_msg_add_empty(msg, "sAMAccountType", LDB_FLAG_MOD_REPLACE, NULL); + if (ret != LDB_SUCCESS) { + talloc_free(tmp_ctx); + ldb_module_oom(module); + return ret; + } + + break; + + case OBJECT_DELETED: + /* + * MS-ADTS 3.1.1.5.5.1.2 Deleted-Object Requirements + * describes what must be removed from a deleted + * object + */ + + ret = ldb_msg_add_empty(msg, "objectCategory", LDB_FLAG_MOD_REPLACE, NULL); + if (ret != LDB_SUCCESS) { + talloc_free(tmp_ctx); + ldb_module_oom(module); + return ret; -- Samba Shared Repository