From e6eaa23582c7800c75198ea4a65f8ff6447e6f98 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Thu, 24 Sep 2015 14:08:37 +1200 Subject: [PATCH 01/36] repl_meta_data: Print more detail into the LDB error string, not just DEBUG() Signed-off-by: Andrew Bartlett --- source4/dsdb/samdb/ldb_modules/repl_meta_data.c | 66 ++++++++++++++++++------- 1 file changed, 49 insertions(+), 17 deletions(-) diff --git a/source4/dsdb/samdb/ldb_modules/repl_meta_data.c b/source4/dsdb/samdb/ldb_modules/repl_meta_data.c index 3310d30..3e4880e 100644 --- a/source4/dsdb/samdb/ldb_modules/repl_meta_data.c +++ b/source4/dsdb/samdb/ldb_modules/repl_meta_data.c @@ -3159,16 +3159,17 @@ static int replmd_delete_internals(struct ldb_module *module, struct ldb_request 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_asprintf_errstring(ldb, __location__ + ": Unable to add a formatted child to dn: %s", + ldb_dn_get_linearized(new_dn)); talloc_free(tmp_ctx); return LDB_ERR_OPERATIONS_ERROR; } ret = ldb_msg_add_string(msg, "isDeleted", "TRUE"); if (ret != LDB_SUCCESS) { - DEBUG(0,(__location__ ": Failed to add isDeleted string to the msg\n")); - ldb_module_oom(module); + ldb_asprintf_errstring(ldb, __location__ + ": Failed to add isDeleted string to the msg"); talloc_free(tmp_ctx); return ret; } @@ -3182,8 +3183,9 @@ static int replmd_delete_internals(struct ldb_module *module, struct ldb_request 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))); + ldb_asprintf_errstring(ldb, __location__ + ": Unable to add a prepare rdn of %s", + ldb_dn_get_linearized(rdn)); talloc_free(tmp_ctx); return LDB_ERR_OPERATIONS_ERROR; } @@ -3191,9 +3193,10 @@ static int replmd_delete_internals(struct ldb_module *module, struct ldb_request 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))); + ldb_asprintf_errstring(ldb, __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; } @@ -3217,29 +3220,48 @@ static int replmd_delete_internals(struct ldb_module *module, struct ldb_request */ if (deletion_state == OBJECT_NOT_DELETED) { + struct ldb_dn *parent_dn = ldb_dn_get_parent(tmp_ctx, old_dn); + char *parent_dn_str = NULL; + /* 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, + parent_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) { ldb_asprintf_errstring(ldb_module_get_ctx(module), - "repmd_delete: Failed to %s %s, because we failed to find it's parent (%s): %s", + "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_dn_get_linearized(parent_dn), ldb_errstring(ldb_module_get_ctx(module))); talloc_free(tmp_ctx); return ret; } + /* + * Now we can use the DB version, + * it will have the extended DN info in it + */ + parent_dn = parent_res->msgs[0]->dn; + parent_dn_str = ldb_dn_get_extended_linearized(tmp_ctx, + parent_dn, + 1); + if (parent_dn_str == NULL) { + talloc_free(tmp_ctx); + return ldb_module_oom(module); + } + ret = ldb_msg_add_steal_string(msg, "lastKnownParent", - ldb_dn_get_extended_linearized(tmp_ctx, parent_res->msgs[0]->dn, 1)); + parent_dn_str); if (ret != LDB_SUCCESS) { - DEBUG(0,(__location__ ": Failed to add lastKnownParent string to the msg\n")); - ldb_module_oom(module); + ldb_asprintf_errstring(ldb, __location__ + ": Failed to add lastKnownParent " + "string when deleting %s", + ldb_dn_get_linearized(old_dn)); talloc_free(tmp_ctx); return ret; } @@ -3248,8 +3270,10 @@ static int replmd_delete_internals(struct ldb_module *module, struct ldb_request 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); + ldb_asprintf_errstring(ldb, __location__ + ": Failed to add msDS-LastKnownRDN " + "string when deleting %s", + ldb_dn_get_linearized(old_dn)); talloc_free(tmp_ctx); return ret; } @@ -3316,6 +3340,14 @@ static int replmd_delete_internals(struct ldb_module *module, struct ldb_request */ ret = replmd_delete_remove_link(module, schema, old_dn, el, sa, req); if (ret != LDB_SUCCESS) { + const char *old_dn_str + = ldb_dn_get_linearized(old_dn); + ldb_asprintf_errstring(ldb, + __location__ + ": Failed to remove backlink of " + "%s when deleting %s", + el->name, + old_dn_str); talloc_free(tmp_ctx); return LDB_ERR_OPERATIONS_ERROR; } From 1822a8a64ef089eb2940680a4287a43f7f5a3769 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Mon, 12 Oct 2015 15:51:37 +1300 Subject: [PATCH 02/36] repl_meta_data: Remove the correct linked attribute forward link The previous code assumed that only plain DNs could be linked attributes. We need to look over the list of attribute values and find the value that causes this particular backlink to exist, so we can remove it. We do not know (until we search) of the binary portion, so we must search over all the attribute values at this layer, using the parsed_dn_find() routine used elsewhere in this code. Found attempting to demote an RODC in a clone of a Windows 2012R2 domain, due to the msDS-RevealedUsers attribute. BUG: https://bugzilla.samba.org/show_bug.cgi?id=11139 Signed-off-by: Andrew Bartlett --- source4/dsdb/samdb/ldb_modules/repl_meta_data.c | 75 +++++++++++++++++++------ 1 file changed, 58 insertions(+), 17 deletions(-) diff --git a/source4/dsdb/samdb/ldb_modules/repl_meta_data.c b/source4/dsdb/samdb/ldb_modules/repl_meta_data.c index 3e4880e..6b469bf 100644 --- a/source4/dsdb/samdb/ldb_modules/repl_meta_data.c +++ b/source4/dsdb/samdb/ldb_modules/repl_meta_data.c @@ -2897,6 +2897,7 @@ static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *are static int replmd_delete_remove_link(struct ldb_module *module, const struct dsdb_schema *schema, struct ldb_dn *dn, + struct GUID *guid, struct ldb_message_element *el, const struct dsdb_attribute *sa, struct ldb_request *parent) @@ -2907,13 +2908,18 @@ static int replmd_delete_remove_link(struct ldb_module *module, for (i=0; inum_values; i++) { struct dsdb_dn *dsdb_dn; - NTSTATUS status; int ret; - struct GUID guid2; struct ldb_message *msg; const struct dsdb_attribute *target_attr; struct ldb_message_element *el2; + const char *dn_str; struct ldb_val dn_val; + const char *attrs[] = { NULL, NULL }; + struct ldb_result *link_res; + struct ldb_message *link_msg; + struct ldb_message_element *link_el; + struct parsed_dn *p; + struct parsed_dn *link_dns; if (dsdb_dn_is_deleted_val(&el->values[i])) { continue; @@ -2925,12 +2931,6 @@ static int replmd_delete_remove_link(struct ldb_module *module, return LDB_ERR_OPERATIONS_ERROR; } - status = dsdb_get_extended_dn_guid(dsdb_dn->dn, &guid2, "GUID"); - if (!NT_STATUS_IS_OK(status)) { - talloc_free(tmp_ctx); - return LDB_ERR_OPERATIONS_ERROR; - } - /* remove the link */ msg = ldb_msg_new(tmp_ctx); if (!msg) { @@ -2946,14 +2946,54 @@ static int replmd_delete_remove_link(struct ldb_module *module, if (target_attr == NULL) { continue; } + attrs[0] = target_attr->lDAPDisplayName; - ret = ldb_msg_add_empty(msg, target_attr->lDAPDisplayName, LDB_FLAG_MOD_DELETE, &el2); + ret = ldb_msg_add_empty(msg, target_attr->lDAPDisplayName, + LDB_FLAG_MOD_DELETE, &el2); if (ret != LDB_SUCCESS) { ldb_module_oom(module); talloc_free(tmp_ctx); return LDB_ERR_OPERATIONS_ERROR; } - dn_val = data_blob_string_const(ldb_dn_get_linearized(dn)); + + ret = dsdb_module_search_dn(module, tmp_ctx, &link_res, + msg->dn, attrs, + DSDB_FLAG_NEXT_MODULE, parent); + + if (ret == LDB_SUCCESS) { + talloc_free(tmp_ctx); + return ret; + } + + link_msg = link_res->msgs[0]; + link_el = ldb_msg_find_element(link_msg, + target_attr->lDAPDisplayName); + + ret = get_parsed_dns(module, tmp_ctx, + link_el, &link_dns, + target_attr->syntax->ldap_oid, parent); + if (ret != LDB_SUCCESS) { + talloc_free(tmp_ctx); + return ret; + } + p = parsed_dn_find(link_dns, link_el->num_values, guid, dn); + if (p == NULL) { + ldb_asprintf_errstring(ldb_module_get_ctx(module), + "Failed to find forward link on %s " + "as %s to remove backlink %s on %s", + ldb_dn_get_linearized(msg->dn), + target_attr->lDAPDisplayName, + sa->lDAPDisplayName, + ldb_dn_get_linearized(dn)); + talloc_free(tmp_ctx); + return LDB_ERR_NO_SUCH_ATTRIBUTE; + } + + + /* This needs to get the Binary DN, by first searching */ + dn_str = dsdb_dn_get_linearized(tmp_ctx, + p->dsdb_dn); + dn_val = data_blob_string_const(dn_str); el2->values = &dn_val; el2->num_values = 1; @@ -3149,10 +3189,10 @@ static int replmd_delete_internals(struct ldb_module *module, struct ldb_request } } - if (deletion_state == OBJECT_NOT_DELETED) { - /* get the objects GUID from the search we just did */ - guid = samdb_result_guid(old_msg, "objectGUID"); + /* get the objects GUID from the search we just did */ + guid = samdb_result_guid(old_msg, "objectGUID"); + if (deletion_state == OBJECT_NOT_DELETED) { /* Add a formatted child */ retb = ldb_dn_add_child_fmt(new_dn, "%s=%s\\0ADEL:%s", rdn_name, @@ -3329,7 +3369,7 @@ static int replmd_delete_internals(struct ldb_module *module, struct ldb_request /* don't remove the rDN */ continue; } - if (sa->linkID && (sa->linkID & 1)) { + if (sa->linkID & 1) { /* we have a backlink in this object that needs to be removed. We're not @@ -3338,7 +3378,9 @@ static int replmd_delete_internals(struct ldb_module *module, struct ldb_request modify to delete the corresponding forward link */ - ret = replmd_delete_remove_link(module, schema, old_dn, el, sa, req); + ret = replmd_delete_remove_link(module, schema, + old_dn, &guid, + el, sa, req); if (ret != LDB_SUCCESS) { const char *old_dn_str = ldb_dn_get_linearized(old_dn); @@ -3356,8 +3398,7 @@ static int replmd_delete_internals(struct ldb_module *module, struct ldb_request directly */ continue; - } - if (!sa->linkID) { + } else if (sa->linkID == 0) { if (ldb_attr_in_list(preserved_attrs, el->name)) { continue; } From fdef8c9a7f810d4048dc644ae473035f0b8a3533 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Tue, 25 Aug 2015 15:51:19 +1200 Subject: [PATCH 03/36] samba-tool: Remove vampire subcommand and now unused libnet_Vampire() This has been deprecated for a long time now Signed-off-by: Andrew Bartlett --- python/samba/netcmd/main.py | 2 - python/samba/netcmd/vampire.py | 55 -------- python/samba/tests/provision.py | 3 - source4/libnet/libnet_vampire.c | 282 ---------------------------------------- source4/libnet/py_net.c | 59 --------- 5 files changed, 401 deletions(-) delete mode 100644 python/samba/netcmd/vampire.py diff --git a/python/samba/netcmd/main.py b/python/samba/netcmd/main.py index 5f788235..471c6b4 100644 --- a/python/samba/netcmd/main.py +++ b/python/samba/netcmd/main.py @@ -37,7 +37,6 @@ from samba.netcmd.testparm import cmd_testparm from samba.netcmd.time import cmd_time from samba.netcmd.user import cmd_user -from samba.netcmd.vampire import cmd_vampire from samba.netcmd.processes import cmd_processes @@ -66,5 +65,4 @@ class cmd_sambatool(SuperCommand): subcommands["testparm"] = cmd_testparm() subcommands["time"] = cmd_time() subcommands["user"] = cmd_user() - subcommands["vampire"] = cmd_vampire() subcommands["processes"] = cmd_processes() diff --git a/python/samba/netcmd/vampire.py b/python/samba/netcmd/vampire.py deleted file mode 100644 index b12222e..0000000 --- a/python/samba/netcmd/vampire.py +++ /dev/null @@ -1,55 +0,0 @@ -# Vampire -# -# Copyright Jelmer Vernooij 2010 -# -# 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 . -# - -import samba.getopt as options - -from samba.net import Net - -from samba.netcmd import ( - Command, - Option, - SuperCommand, - CommandError - ) - - -class cmd_vampire(Command): - """Join and synchronise a remote AD domain to the local server.""" - synopsis = "%prog [options] " - - takes_optiongroups = { - "sambaopts": options.SambaOptions, - "credopts": options.CredentialsOptions, - "versionopts": options.VersionOptions, - } - - takes_options = [ - Option("--target-dir", help="Target directory.", type=str), - Option("--force", help="force run", action='store_true', default=False), - ] - - takes_args = ["domain"] - - def run(self, domain, target_dir=None, credopts=None, sambaopts=None, versionopts=None, force=False): - if not force: - raise CommandError("samba-tool vampire is deprecated, please use samba-tool domain join. Use --force to override") - lp = sambaopts.get_loadparm() - creds = credopts.get_credentials(lp) - net = Net(creds, lp, server=credopts.ipaddress) - (domain_name, domain_sid) = net.vampire(domain=domain, target_dir=target_dir) - self.outf.write("Vampired domain %s (%s)\n" % (domain_name, domain_sid)) diff --git a/python/samba/tests/provision.py b/python/samba/tests/provision.py index 67f8c18..91c66d5 100644 --- a/python/samba/tests/provision.py +++ b/python/samba/tests/provision.py @@ -116,9 +116,6 @@ def test_provision_guess(self): def test_join_domain(self): raise NotImplementedError(self.test_join_domain) - def test_vampire(self): - raise NotImplementedError(self.test_vampire) - class SanitizeServerRoleTests(TestCase): diff --git a/source4/libnet/libnet_vampire.c b/source4/libnet/libnet_vampire.c index 69195af..1c3403f 100644 --- a/source4/libnet/libnet_vampire.c +++ b/source4/libnet/libnet_vampire.c @@ -748,285 +748,3 @@ NTSTATUS libnet_vampire_cb_store_chunk(void *private_data, return NT_STATUS_OK; } -static NTSTATUS update_dnshostname_for_server(TALLOC_CTX *mem_ctx, - struct ldb_context *ldb, - const char *server_dn_str, - const char *netbios_name, - const char *realm) -{ - int ret; - struct ldb_message *msg; - struct ldb_message_element *el; - struct ldb_dn *server_dn; - const char *dNSHostName = strlower_talloc(mem_ctx, - talloc_asprintf(mem_ctx, - "%s.%s", - netbios_name, - realm)); - msg = ldb_msg_new(mem_ctx); - if (msg == NULL) { - return NT_STATUS_NO_MEMORY; - } - - server_dn = ldb_dn_new(mem_ctx, ldb, server_dn_str); - if (!server_dn) { - return NT_STATUS_INTERNAL_ERROR; - } - - msg->dn = server_dn; - ret = ldb_msg_add_empty(msg, "dNSHostName", LDB_FLAG_MOD_ADD, &el); - if (ret != LDB_SUCCESS) { - return NT_STATUS_INTERNAL_ERROR; - } - - ret = ldb_msg_add_steal_string(msg, - "dNSHostName", - talloc_asprintf(el->values, "%s", dNSHostName)); - if (ret != LDB_SUCCESS) { - return NT_STATUS_INTERNAL_ERROR; - } - - ret = dsdb_modify(ldb, msg, DSDB_MODIFY_PERMISSIVE); - if (ret != LDB_SUCCESS) { - DEBUG(0,(__location__ ": Failed to add dnsHostName to the Server object: %s\n", - ldb_errstring(ldb))); - return NT_STATUS_INTERNAL_ERROR; - } - - return NT_STATUS_OK; -} - - -NTSTATUS libnet_Vampire(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, - struct libnet_Vampire *r) -{ - struct libnet_JoinDomain *join; - struct libnet_Replicate rep; - NTSTATUS status; - - const char *account_name; - const char *netbios_name; - - r->out.error_string = NULL; - - join = talloc_zero(mem_ctx, struct libnet_JoinDomain); - if (!join) { - return NT_STATUS_NO_MEMORY; - } - - if (r->in.netbios_name != NULL) { - netbios_name = r->in.netbios_name; - } else { - netbios_name = talloc_reference(join, lpcfg_netbios_name(ctx->lp_ctx)); - if (!netbios_name) { - talloc_free(join); - r->out.error_string = NULL; - return NT_STATUS_NO_MEMORY; - } - } - - account_name = talloc_asprintf(join, "%s$", netbios_name); - if (!account_name) { - talloc_free(join); - r->out.error_string = NULL; - return NT_STATUS_NO_MEMORY; - } - - /* Re-use the domain we are joining as the domain for the user - * to be authenticated with, unless they specified - * otherwise */ - cli_credentials_set_domain(ctx->cred, r->in.domain_name, CRED_GUESS_ENV); - - join->in.domain_name = r->in.domain_name; - join->in.account_name = account_name; - join->in.netbios_name = netbios_name; - join->in.level = LIBNET_JOINDOMAIN_AUTOMATIC; - join->in.acct_type = ACB_WSTRUST; - join->in.recreate_account = false; - status = libnet_JoinDomain(ctx, join, join); - if (!NT_STATUS_IS_OK(status)) { - r->out.error_string = talloc_steal(mem_ctx, join->out.error_string); - talloc_free(join); - return status; - } - - rep.in.domain_name = join->out.domain_name; - rep.in.netbios_name = netbios_name; - rep.in.targetdir = r->in.targetdir; - rep.in.domain_sid = join->out.domain_sid; - rep.in.realm = join->out.realm; - rep.in.server = dcerpc_binding_get_string_option(join->out.samr_binding, - "host"); - rep.in.join_password = join->out.join_password; - rep.in.kvno = join->out.kvno; - - status = libnet_Replicate(ctx, mem_ctx, &rep); - - r->out.domain_sid = join->out.domain_sid; - r->out.domain_name = join->out.domain_name; - r->out.error_string = rep.out.error_string; - - return status; -} - - - -NTSTATUS libnet_Replicate(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, - struct libnet_Replicate *r) -{ - struct provision_store_self_join_settings *set_secrets; - struct libnet_BecomeDC b; - struct libnet_vampire_cb_state *s; - struct ldb_message *msg; - const char *error_string; - int ldb_ret; - uint32_t i; - NTSTATUS status; - TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx); - const char *account_name; - const char *netbios_name; - - r->out.error_string = NULL; - - netbios_name = r->in.netbios_name; - account_name = talloc_asprintf(tmp_ctx, "%s$", netbios_name); - if (!account_name) { - talloc_free(tmp_ctx); - r->out.error_string = NULL; - return NT_STATUS_NO_MEMORY; - } - - /* Re-use the domain we are joining as the domain for the user - * to be authenticated with, unless they specified - * otherwise */ - cli_credentials_set_domain(ctx->cred, r->in.domain_name, CRED_GUESS_ENV); - - s = libnet_vampire_cb_state_init(mem_ctx, ctx->lp_ctx, ctx->event_ctx, - netbios_name, r->in.domain_name, r->in.realm, - r->in.targetdir); - if (!s) { - return NT_STATUS_NO_MEMORY; - } - talloc_steal(s, tmp_ctx); - - ZERO_STRUCT(b); - - /* Be more robust: - * We now know the domain and realm for sure - if they didn't - * put one on the command line, use this for the rest of the - * join */ - cli_credentials_set_realm(ctx->cred, r->in.realm, CRED_GUESS_ENV); - cli_credentials_set_domain(ctx->cred, r->in.domain_name, CRED_GUESS_ENV); - - /* Now set these values into the smb.conf - we probably had - * empty or useless defaults here from whatever smb.conf we - * started with */ - lpcfg_set_cmdline(s->lp_ctx, "realm", r->in.realm); - lpcfg_set_cmdline(s->lp_ctx, "workgroup", r->in.domain_name); - - b.in.domain_dns_name = r->in.realm; - b.in.domain_netbios_name = r->in.domain_name; - b.in.domain_sid = r->in.domain_sid; - b.in.source_dsa_address = r->in.server; - b.in.dest_dsa_netbios_name = netbios_name; - - b.in.callbacks.private_data = s; - b.in.callbacks.check_options = libnet_vampire_cb_check_options; - b.in.callbacks.prepare_db = libnet_vampire_cb_prepare_db; - b.in.callbacks.schema_chunk = libnet_vampire_cb_schema_chunk; - b.in.callbacks.config_chunk = libnet_vampire_cb_store_chunk; - b.in.callbacks.domain_chunk = libnet_vampire_cb_store_chunk; - - b.in.rodc_join = lpcfg_parm_bool(s->lp_ctx, NULL, "repl", "RODC", false); - - status = libnet_BecomeDC(ctx, s, &b); - if (!NT_STATUS_IS_OK(status)) { - printf("libnet_BecomeDC() failed - %s\n", nt_errstr(status)); - talloc_free(s); - return status; - } - - msg = ldb_msg_new(s); - if (!msg) { - printf("ldb_msg_new() failed\n"); - talloc_free(s); - return NT_STATUS_NO_MEMORY; - } - msg->dn = ldb_dn_new(msg, s->ldb, "@ROOTDSE"); - if (!msg->dn) { - printf("ldb_msg_new(@ROOTDSE) failed\n"); - talloc_free(s); - return NT_STATUS_NO_MEMORY; - } - - ldb_ret = ldb_msg_add_string(msg, "isSynchronized", "TRUE"); - if (ldb_ret != LDB_SUCCESS) { - printf("ldb_msg_add_string(msg, isSynchronized, TRUE) failed: %d\n", ldb_ret); - talloc_free(s); - return NT_STATUS_NO_MEMORY; - } - - for (i=0; i < msg->num_elements; i++) { - msg->elements[i].flags = LDB_FLAG_MOD_REPLACE; - } - - printf("mark ROOTDSE with isSynchronized=TRUE\n"); - ldb_ret = ldb_modify(s->ldb, msg); - if (ldb_ret != LDB_SUCCESS) { - printf("ldb_modify() failed: %d : %s\n", ldb_ret, ldb_errstring(s->ldb)); - talloc_free(s); - return NT_STATUS_INTERNAL_DB_ERROR; - } - /* during dcpromo the 2nd computer adds dNSHostName attribute to his Server object - * the attribute appears on the original DC after replication - */ - status = update_dnshostname_for_server(s, s->ldb, s->server_dn_str, s->netbios_name, s->realm); - if (!NT_STATUS_IS_OK(status)) { - printf("Failed to update dNSHostName on Server object - %s\n", nt_errstr(status)); - talloc_free(s); - return status; - } - /* prepare the transaction - this prepares to commit all the changes in - the ldb from the whole vampire. Note that this - triggers the writing of the linked attribute backlinks. - */ - if (ldb_transaction_prepare_commit(s->ldb) != LDB_SUCCESS) { - printf("Failed to prepare_commit vampire transaction: %s\n", ldb_errstring(s->ldb)); - return NT_STATUS_INTERNAL_DB_ERROR; - } - - set_secrets = talloc(s, struct provision_store_self_join_settings); - if (!set_secrets) { - r->out.error_string = NULL; - talloc_free(s); - return NT_STATUS_NO_MEMORY; - } - - ZERO_STRUCTP(set_secrets); - set_secrets->domain_name = r->in.domain_name; - set_secrets->realm = r->in.realm; - set_secrets->netbios_name = netbios_name; - set_secrets->secure_channel_type = SEC_CHAN_BDC; - set_secrets->machine_password = r->in.join_password; - set_secrets->key_version_number = r->in.kvno; - set_secrets->domain_sid = r->in.domain_sid; - - status = provision_store_self_join(ctx, s->lp_ctx, ctx->event_ctx, set_secrets, &error_string); - if (!NT_STATUS_IS_OK(status)) { - r->out.error_string = talloc_steal(mem_ctx, error_string); - talloc_free(s); - return status; - } - - /* commit the transaction now we know the secrets were written - * out properly - */ - if (ldb_transaction_commit(s->ldb) != LDB_SUCCESS) { - printf("Failed to commit vampire transaction\n"); - return NT_STATUS_INTERNAL_DB_ERROR; - } - - talloc_free(s); - - return NT_STATUS_OK; -} diff --git a/source4/libnet/py_net.c b/source4/libnet/py_net.c index 3fcbf05..48009b2 100644 --- a/source4/libnet/py_net.c +++ b/source4/libnet/py_net.c @@ -293,61 +293,6 @@ static PyObject *py_net_user_delete(py_net_Object *self, PyObject *args, PyObjec static const char py_net_delete_user_doc[] = "delete_user(username)\n" "Delete a user."; -static PyObject *py_dom_sid_FromSid(struct dom_sid *sid) -{ - PyObject *mod_security, *dom_sid_Type; - - mod_security = PyImport_ImportModule("samba.dcerpc.security"); - if (mod_security == NULL) - return NULL; - - dom_sid_Type = PyObject_GetAttrString(mod_security, "dom_sid"); - if (dom_sid_Type == NULL) - return NULL; - - return pytalloc_reference((PyTypeObject *)dom_sid_Type, sid); -} - -static PyObject *py_net_vampire(py_net_Object *self, PyObject *args, PyObject *kwargs) -{ - const char *kwnames[] = { "domain", "target_dir", NULL }; - NTSTATUS status; - TALLOC_CTX *mem_ctx; - PyObject *ret; - struct libnet_Vampire r; - - ZERO_STRUCT(r); - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|z", discard_const_p(char *, kwnames), - &r.in.domain_name, &r.in.targetdir)) { - return NULL; - } - - r.in.netbios_name = lpcfg_netbios_name(self->libnet_ctx->lp_ctx); - r.out.error_string = NULL; - - mem_ctx = talloc_new(NULL); - if (mem_ctx == NULL) { - PyErr_NoMemory(); - return NULL; - } - - status = libnet_Vampire(self->libnet_ctx, mem_ctx, &r); - - if (!NT_STATUS_IS_OK(status)) { - PyErr_SetString(PyExc_RuntimeError, - r.out.error_string ? r.out.error_string : nt_errstr(status)); - talloc_free(mem_ctx); - return NULL; - } - - ret = Py_BuildValue("(sO)", r.out.domain_name, py_dom_sid_FromSid(r.out.domain_sid)); - - talloc_free(mem_ctx); - - return ret; -} - struct replicate_state { void *vampire_state; dcerpc_InterfaceObject *drs_pipe; @@ -593,9 +538,6 @@ static PyObject *py_net_finddc(py_net_Object *self, PyObject *args, PyObject *kw } -static const char py_net_vampire_doc[] = "vampire(domain, target_dir=None)\n" - "Vampire a domain."; - static const char py_net_replicate_init_doc[] = "replicate_init(samdb, lp, drspipe)\n" "Setup for replicate_chunk calls."; @@ -612,7 +554,6 @@ static PyMethodDef net_obj_methods[] = { {"time", (PyCFunction)py_net_time, METH_VARARGS|METH_KEYWORDS, py_net_time_doc}, {"create_user", (PyCFunction)py_net_user_create, METH_VARARGS|METH_KEYWORDS, py_net_create_user_doc}, {"delete_user", (PyCFunction)py_net_user_delete, METH_VARARGS|METH_KEYWORDS, py_net_delete_user_doc}, - {"vampire", (PyCFunction)py_net_vampire, METH_VARARGS|METH_KEYWORDS, py_net_vampire_doc}, {"replicate_init", (PyCFunction)py_net_replicate_init, METH_VARARGS|METH_KEYWORDS, py_net_replicate_init_doc}, {"replicate_chunk", (PyCFunction)py_net_replicate_chunk, METH_VARARGS|METH_KEYWORDS, py_net_replicate_chunk_doc}, {"finddc", (PyCFunction)py_net_finddc, METH_KEYWORDS, py_net_finddc_doc}, From 24380f6cd9de1a1c3b36f11b539f4e73a6c00aea Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Mon, 17 Aug 2015 15:33:31 +1200 Subject: [PATCH 04/36] samba-tool: Add new command 'samba-tool drs clone-dc-database' This command makes a clone of an existing AD Domain, but does not join the domain. This allows us to test if the join would work without adding objects to the target DC. The server password will need to be reset for the clone to be any use, see the source4/scripting/devel/chgtdcpass (Based on patches written with Garming Sam) Andrew Bartlett Signed-off-by: Andrew Bartlett Signed-off-by: Garming Sam --- python/samba/join.py | 142 +++++++++++++++++--------- python/samba/netcmd/drs.py | 41 ++++++++ python/samba/tests/__init__.py | 2 +- python/samba/tests/blackbox/samba_tool_drs.py | 33 +++++- 4 files changed, 165 insertions(+), 53 deletions(-) diff --git a/python/samba/join.py b/python/samba/join.py index c356145..74c14c2 100644 --- a/python/samba/join.py +++ b/python/samba/join.py @@ -54,12 +54,13 @@ class dc_join(object): def __init__(ctx, logger=None, server=None, creds=None, lp=None, site=None, netbios_name=None, targetdir=None, domain=None, machinepass=None, use_ntvfs=False, dns_backend=None, - promote_existing=False): + promote_existing=False, clone_only=False): + ctx.clone_only=clone_only + ctx.logger = logger ctx.creds = creds ctx.lp = lp ctx.site = site - ctx.netbios_name = netbios_name ctx.targetdir = targetdir ctx.use_ntvfs = use_ntvfs @@ -89,8 +90,6 @@ def __init__(ctx, logger=None, server=None, creds=None, lp=None, site=None, raise DCJoinException(estr) - ctx.myname = netbios_name - ctx.samname = "%s$" % ctx.myname ctx.base_dn = str(ctx.samdb.get_default_basedn()) ctx.root_dn = str(ctx.samdb.get_root_basedn()) ctx.schema_dn = str(ctx.samdb.get_schema_basedn()) @@ -110,17 +109,34 @@ def __init__(ctx, logger=None, server=None, creds=None, lp=None, site=None, else: ctx.acct_pass = samba.generate_random_password(32, 40) - # work out the DNs of all the objects we will be adding - ctx.server_dn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (ctx.myname, ctx.site, ctx.config_dn) - ctx.ntds_dn = "CN=NTDS Settings,%s" % ctx.server_dn - topology_base = "CN=Topology,CN=Domain System Volume,CN=DFSR-GlobalSettings,CN=System,%s" % ctx.base_dn - if ctx.dn_exists(topology_base): - ctx.topology_dn = "CN=%s,%s" % (ctx.myname, topology_base) + ctx.dnsdomain = ctx.samdb.domain_dns_name() + if clone_only: + # As we don't want to create or delete these DNs, we set them to None + ctx.server_dn = None + ctx.ntds_dn = None + ctx.acct_dn = None + ctx.myname = ctx.server.split('.')[0] + ctx.ntds_guid = None else: - ctx.topology_dn = None + # work out the DNs of all the objects we will be adding + ctx.myname = netbios_name + ctx.samname = "%s$" % ctx.myname + ctx.server_dn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (ctx.myname, ctx.site, ctx.config_dn) + ctx.ntds_dn = "CN=NTDS Settings,%s" % ctx.server_dn + ctx.acct_dn = "CN=%s,OU=Domain Controllers,%s" % (ctx.myname, ctx.base_dn) + ctx.dnshostname = "%s.%s" % (ctx.myname.lower(), ctx.dnsdomain) + ctx.dnsforest = ctx.samdb.forest_dns_name() + + topology_base = "CN=Topology,CN=Domain System Volume,CN=DFSR-GlobalSettings,CN=System,%s" % ctx.base_dn + if ctx.dn_exists(topology_base): + ctx.topology_dn = "CN=%s,%s" % (ctx.myname, topology_base) + else: + ctx.topology_dn = None + + ctx.SPNs = [ "HOST/%s" % ctx.myname, + "HOST/%s" % ctx.dnshostname, + "GC/%s/%s" % (ctx.dnshostname, ctx.dnsforest) ] - ctx.dnsdomain = ctx.samdb.domain_dns_name() - ctx.dnsforest = ctx.samdb.forest_dns_name() ctx.domaindns_zone = 'DC=DomainDnsZones,%s' % ctx.base_dn ctx.forestdns_zone = 'DC=ForestDnsZones,%s' % ctx.root_dn @@ -137,18 +153,10 @@ def __init__(ctx, logger=None, server=None, creds=None, lp=None, site=None, else: ctx.dns_backend = dns_backend - ctx.dnshostname = "%s.%s" % (ctx.myname.lower(), ctx.dnsdomain) - ctx.realm = ctx.dnsdomain - ctx.acct_dn = "CN=%s,OU=Domain Controllers,%s" % (ctx.myname, ctx.base_dn) - ctx.tmp_samdb = None - ctx.SPNs = [ "HOST/%s" % ctx.myname, - "HOST/%s" % ctx.dnshostname, - "GC/%s/%s" % (ctx.dnshostname, ctx.dnsforest) ] - # these elements are optional ctx.never_reveal_sid = None ctx.reveal_sid = None @@ -538,28 +546,30 @@ def join_add_objects(ctx): if ctx.krbtgt_dn: ctx.add_krbtgt_account() - print "Adding %s" % ctx.server_dn - rec = { - "dn": ctx.server_dn, - "objectclass" : "server", - # windows uses 50000000 decimal for systemFlags. A windows hex/decimal mixup bug? - "systemFlags" : str(samba.dsdb.SYSTEM_FLAG_CONFIG_ALLOW_RENAME | - samba.dsdb.SYSTEM_FLAG_CONFIG_ALLOW_LIMITED_MOVE | - samba.dsdb.SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE), - # windows seems to add the dnsHostName later - "dnsHostName" : ctx.dnshostname} - - if ctx.acct_dn: - rec["serverReference"] = ctx.acct_dn + if ctx.server_dn: + print "Adding %s" % ctx.server_dn + rec = { + "dn": ctx.server_dn, + "objectclass" : "server", + # windows uses 50000000 decimal for systemFlags. A windows hex/decimal mixup bug? + "systemFlags" : str(samba.dsdb.SYSTEM_FLAG_CONFIG_ALLOW_RENAME | + samba.dsdb.SYSTEM_FLAG_CONFIG_ALLOW_LIMITED_MOVE | + samba.dsdb.SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE), + # windows seems to add the dnsHostName later + "dnsHostName" : ctx.dnshostname} + + if ctx.acct_dn: + rec["serverReference"] = ctx.acct_dn - ctx.samdb.add(rec) + ctx.samdb.add(rec) if ctx.subdomain: # the rest is done after replication ctx.ntds_guid = None return - ctx.join_add_ntdsdsa() + if ctx.ntds_dn: + ctx.join_add_ntdsdsa() if ctx.connection_dn is not None: print "Adding %s" % ctx.connection_dn @@ -876,15 +886,17 @@ def join_finalise(ctx): """Finalise the join, mark us synchronised and setup secrets db.""" # FIXME we shouldn't do this in all cases + # If for some reasons we joined in another site than the one of # DC we just replicated from then we don't need to send the updatereplicateref # as replication between sites is time based and on the initiative of the # requesting DC - ctx.logger.info("Sending DsReplicaUpdateRefs for all the replicated partitions") - for nc in ctx.nc_list: - ctx.send_DsReplicaUpdateRefs(nc) + if not ctx.clone_only: + ctx.logger.info("Sending DsReplicaUpdateRefs for all the replicated partitions") + for nc in ctx.nc_list: + ctx.send_DsReplicaUpdateRefs(nc) - if ctx.RODC: + if not ctx.clone_only and ctx.RODC: print "Setting RODC invocationId" ctx.local_samdb.set_invocation_id(str(ctx.invocation_id)) ctx.local_samdb.set_opaque_integer("domainFunctionality", @@ -914,11 +926,18 @@ def join_finalise(ctx): m = ldb.Message() m.dn = ldb.Dn(ctx.local_samdb, '@ROOTDSE') m["isSynchronized"] = ldb.MessageElement("TRUE", ldb.FLAG_MOD_REPLACE, "isSynchronized") - m["dsServiceName"] = ldb.MessageElement("" % str(ctx.ntds_guid), + + # We want to appear to be the server we just cloned + if ctx.clone_only: + guid = ctx.samdb.get_ntds_GUID() + else: + guid = ctx.ntds_guid + + m["dsServiceName"] = ldb.MessageElement("" % str(guid), ldb.FLAG_MOD_REPLACE, "dsServiceName") ctx.local_samdb.modify(m) - if ctx.subdomain: + if ctx.clone_only or ctx.subdomain: return secrets_ldb = Ldb(ctx.paths.secrets, session_info=system_session(), lp=ctx.lp) @@ -1077,23 +1096,26 @@ def do_join(ctx): ctx.full_nc_list += [ctx.domaindns_zone] ctx.full_nc_list += [ctx.forestdns_zone] - if ctx.promote_existing: - ctx.promote_possible() - else: - ctx.cleanup_old_join() + if not ctx.clone_only: + if ctx.promote_existing: + ctx.promote_possible() + else: + ctx.cleanup_old_join() try: - ctx.join_add_objects() + if not ctx.clone_only: + ctx.join_add_objects() ctx.join_provision() ctx.join_replicate() - if ctx.subdomain: + if (not ctx.clone_only and ctx.subdomain): ctx.join_add_objects2() ctx.join_provision_own_domain() ctx.join_setup_trusts() ctx.join_finalise() except: print "Join failed - cleaning up" - ctx.cleanup_old_join() + if not ctx.clone_only: + ctx.cleanup_old_join() raise @@ -1183,6 +1205,28 @@ def join_DC(logger=None, server=None, creds=None, lp=None, site=None, netbios_na ctx.do_join() logger.info("Joined domain %s (SID %s) as a DC" % (ctx.domain_name, ctx.domsid)) +def join_clone(logger=None, server=None, creds=None, lp=None, + targetdir=None, domain=None): + """Join as a DC.""" + ctx = dc_join(logger, server, creds, lp, site=None, netbios_name=None, targetdir=targetdir, domain=domain, + machinepass=None, use_ntvfs=False, dns_backend="NONE", promote_existing=False, clone_only=True) + + lp.set("workgroup", ctx.domain_name) + logger.info("workgroup is %s" % ctx.domain_name) + + lp.set("realm", ctx.realm) + logger.info("realm is %s" % ctx.realm) + + ctx.replica_flags = (drsuapi.DRSUAPI_DRS_WRIT_REP | + drsuapi.DRSUAPI_DRS_INIT_SYNC | + drsuapi.DRSUAPI_DRS_PER_SYNC | + drsuapi.DRSUAPI_DRS_FULL_SYNC_IN_PROGRESS | + drsuapi.DRSUAPI_DRS_NEVER_SYNCED) + ctx.domain_replica_flags = ctx.replica_flags + + ctx.do_join() + logger.info("Cloned domain %s (SID %s)" % (ctx.domain_name, ctx.domsid)) + def join_subdomain(logger=None, server=None, creds=None, lp=None, site=None, netbios_name=None, targetdir=None, parent_domain=None, dnsdomain=None, netbios_domain=None, machinepass=None, adminpass=None, use_ntvfs=False, diff --git a/python/samba/netcmd/drs.py b/python/samba/netcmd/drs.py index e8e9ec8..f1d4970 100644 --- a/python/samba/netcmd/drs.py +++ b/python/samba/netcmd/drs.py @@ -20,6 +20,7 @@ import samba.getopt as options import ldb +import logging from samba.auth import system_session from samba.netcmd import ( @@ -32,6 +33,7 @@ from samba import drs_utils, nttime2string, dsdb from samba.dcerpc import drsuapi, misc import common +from samba.join import join_clone def drsuapi_connect(ctx): '''make a DRSUAPI connection to the server''' @@ -513,6 +515,44 @@ def run(self, DC=None, dsa_option=None, self.message("New DSA options: " + ", ".join(cur_opts)) +class cmd_drs_clone_dc_database(Command): + """Replicate an initial clone of domain, but DO NOT JOIN it.""" + + synopsis = "%prog [options]" + + takes_optiongroups = { + "sambaopts": options.SambaOptions, + "versionopts": options.VersionOptions, + "credopts": options.CredentialsOptions, + } + + takes_options = [ + Option("--server", help="DC to join", type=str), + Option("--targetdir", help="where to store provision", type=str), + Option("--quiet", help="Be quiet", action="store_true"), + Option("--verbose", help="Be verbose", action="store_true") + ] + + takes_args = ["domain"] + + def run(self, domain, sambaopts=None, credopts=None, + versionopts=None, server=None, targetdir=None, + quiet=False, verbose=False): + lp = sambaopts.get_loadparm() + creds = credopts.get_credentials(lp) + + logger = self.get_logger() + if verbose: + logger.setLevel(logging.DEBUG) + elif quiet: + logger.setLevel(logging.WARNING) + else: + logger.setLevel(logging.INFO) + + join_clone(logger=logger, server=server, creds=creds, lp=lp, domain=domain, + targetdir=targetdir) + + class cmd_drs(SuperCommand): """Directory Replication Services (DRS) management.""" @@ -522,3 +562,4 @@ class cmd_drs(SuperCommand): subcommands["replicate"] = cmd_drs_replicate() subcommands["showrepl"] = cmd_drs_showrepl() subcommands["options"] = cmd_drs_options() + subcommands["clone-dc-database"] = cmd_drs_clone_dc_database() diff --git a/python/samba/tests/__init__.py b/python/samba/tests/__init__.py index b53c4ea..87b6943 100644 --- a/python/samba/tests/__init__.py +++ b/python/samba/tests/__init__.py @@ -253,7 +253,7 @@ def __str__(self): return "Command '%s'; exit status %d; stdout: '%s'; stderr: '%s'" % (self.cmd, self.returncode, self.stdout, self.stderr) -class BlackboxTestCase(TestCase): +class BlackboxTestCase(TestCaseInTempDir): """Base test case for blackbox tests.""" def _make_cmdline(self, line): diff --git a/python/samba/tests/blackbox/samba_tool_drs.py b/python/samba/tests/blackbox/samba_tool_drs.py index 9b7106f..a7bcb4f 100644 --- a/python/samba/tests/blackbox/samba_tool_drs.py +++ b/python/samba/tests/blackbox/samba_tool_drs.py @@ -18,7 +18,8 @@ """Blackbox tests for samba-tool drs.""" import samba.tests - +import shutil +import os class SambaToolDrsTests(samba.tests.BlackboxTestCase): """Blackbox test case for samba-tool drs.""" @@ -33,10 +34,10 @@ def setUp(self): self.cmdline_creds = "-U%s/%s%%%s" % (creds.get_domain(), creds.get_username(), creds.get_password()) - def _get_rootDSE(self, dc): + def _get_rootDSE(self, dc, ldap_only=True): samdb = samba.tests.connect_samdb(dc, lp=self.get_loadparm(), credentials=self.get_credentials(), - ldap_only=True) + ldap_only=ldap_only) return samdb.search(base="", scope=samba.tests.ldb.SCOPE_BASE)[0] def test_samba_tool_bind(self): @@ -100,3 +101,29 @@ def test_samba_tool_replicate(self): self.cmdline_creds)) self.assertTrue("Replicate from" in out) self.assertTrue("was successful" in out) + + def test_samba_tool_drs_clone_dc(self): + """Tests 'samba-tool drs clone-dc-database' command.""" + server_rootdse = self._get_rootDSE(self.dc1) + server_nc_name = server_rootdse["defaultNamingContext"] + server_ds_name = server_rootdse["dsServiceName"] + server_ldap_service_name = str(server_rootdse["ldapServiceName"][0]) + server_realm = server_ldap_service_name.split(":")[0] + creds = self.get_credentials() + out = self.check_output("samba-tool drs clone-dc-database %s --server=%s %s --targetdir=%s" + % (server_realm, + self.dc1, + self.cmdline_creds, + self.tempdir)) + ldb_rootdse = self._get_rootDSE("tdb://" + os.path.join(self.tempdir, "private", "sam.ldb"), ldap_only=False) + nc_name = ldb_rootdse["defaultNamingContext"] + ds_name = ldb_rootdse["dsServiceName"] + ldap_service_name = str(server_rootdse["ldapServiceName"][0]) + self.assertEqual(nc_name, server_nc_name) + # The clone should pretend to be the source server + self.assertEqual(ds_name, server_ds_name) + self.assertEqual(ldap_service_name, server_ldap_service_name) + shutil.rmtree(os.path.join(self.tempdir, "private")) + shutil.rmtree(os.path.join(self.tempdir, "etc")) + shutil.rmtree(os.path.join(self.tempdir, "msg")) + shutil.rmtree(os.path.join(self.tempdir, "state")) From 1da7eee0a64bba80148b6468d20b17d13c0f918c Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Wed, 19 Aug 2015 13:26:41 +1200 Subject: [PATCH 05/36] repl: Give an error if we get a secret when not expecting one We should never get a secret from a server when we specify DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING This asserts that this is the case. Signed-off-by: Andrew Bartlett --- libcli/drsuapi/drsuapi.h | 1 + libcli/drsuapi/repl_decrypt.c | 6 ++++++ source3/libnet/libnet_dssync.c | 1 + source4/dsdb/repl/drepl_out_helpers.c | 3 +++ source4/dsdb/repl/replicated_objects.c | 23 ++++++++++++++++++++--- source4/dsdb/samdb/samdb.h | 1 + source4/libnet/libnet_vampire.c | 7 ++++++- 7 files changed, 38 insertions(+), 4 deletions(-) diff --git a/libcli/drsuapi/drsuapi.h b/libcli/drsuapi/drsuapi.h index a4fb15f..7c6cf2f 100644 --- a/libcli/drsuapi/drsuapi.h +++ b/libcli/drsuapi/drsuapi.h @@ -29,6 +29,7 @@ WERROR drsuapi_decrypt_attribute_value(TALLOC_CTX *mem_ctx, WERROR drsuapi_decrypt_attribute(TALLOC_CTX *mem_ctx, const DATA_BLOB *gensec_skey, uint32_t rid, + uint32_t dsdb_repl_flags, struct drsuapi_DsReplicaAttribute *attr); diff --git a/libcli/drsuapi/repl_decrypt.c b/libcli/drsuapi/repl_decrypt.c index 00b8db8..4a2a28f 100644 --- a/libcli/drsuapi/repl_decrypt.c +++ b/libcli/drsuapi/repl_decrypt.c @@ -28,6 +28,7 @@ #include "../lib/crypto/crypto.h" #include "../libcli/drsuapi/drsuapi.h" #include "libcli/auth/libcli_auth.h" +#include "dsdb/samdb/samdb.h" WERROR drsuapi_decrypt_attribute_value(TALLOC_CTX *mem_ctx, const DATA_BLOB *gensec_skey, @@ -134,6 +135,7 @@ WERROR drsuapi_decrypt_attribute_value(TALLOC_CTX *mem_ctx, WERROR drsuapi_decrypt_attribute(TALLOC_CTX *mem_ctx, const DATA_BLOB *gensec_skey, uint32_t rid, + uint32_t dsdb_repl_flags, struct drsuapi_DsReplicaAttribute *attr) { WERROR status; @@ -164,6 +166,10 @@ WERROR drsuapi_decrypt_attribute(TALLOC_CTX *mem_ctx, return WERR_OK; } + if (dsdb_repl_flags & DSDB_REPL_FLAG_EXPECT_NO_SECRETS) { + return WERR_TOO_MANY_SECRETS; + } + if (attr->value_ctr.num_values > 1) { return WERR_DS_DRA_INVALID_PARAMETER; } diff --git a/source3/libnet/libnet_dssync.c b/source3/libnet/libnet_dssync.c index 94f0628..267709e 100644 --- a/source3/libnet/libnet_dssync.c +++ b/source3/libnet/libnet_dssync.c @@ -113,6 +113,7 @@ static void libnet_dssync_decrypt_attributes(TALLOC_CTX *mem_ctx, drsuapi_decrypt_attribute(mem_ctx, session_key, rid, + 0, attr); } } diff --git a/source4/dsdb/repl/drepl_out_helpers.c b/source4/dsdb/repl/drepl_out_helpers.c index a047881..a1e8dcb 100644 --- a/source4/dsdb/repl/drepl_out_helpers.c +++ b/source4/dsdb/repl/drepl_out_helpers.c @@ -740,6 +740,9 @@ static void dreplsrv_op_pull_source_apply_changes_trigger(struct tevent_req *req if (state->op->options & DRSUAPI_DRS_FULL_SYNC_IN_PROGRESS) { dsdb_repl_flags |= DSDB_REPL_FLAG_PRIORITISE_INCOMING; } + if (state->op->options & DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING) { + dsdb_repl_flags |= DSDB_REPL_FLAG_EXPECT_NO_SECRETS; + } status = dsdb_replicated_objects_convert(service->samdb, working_schema ? working_schema : schema, diff --git a/source4/dsdb/repl/replicated_objects.c b/source4/dsdb/repl/replicated_objects.c index df880ad..1afdb36 100644 --- a/source4/dsdb/repl/replicated_objects.c +++ b/source4/dsdb/repl/replicated_objects.c @@ -347,7 +347,7 @@ WERROR dsdb_convert_object_ex(struct ldb_context *ldb, struct dsdb_extended_replicated_object *out) { NTSTATUS nt_status; - WERROR status; + WERROR status = WERR_OK; uint32_t i; struct ldb_message *msg; struct replPropertyMetaDataBlob *md; @@ -444,8 +444,25 @@ WERROR dsdb_convert_object_ex(struct ldb_context *ldb, } for (j=0; jvalue_ctr.num_values; j++) { - status = drsuapi_decrypt_attribute(a->value_ctr.values[j].blob, gensec_skey, rid, a); - W_ERROR_NOT_OK_RETURN(status); + status = drsuapi_decrypt_attribute(a->value_ctr.values[j].blob, + gensec_skey, rid, + dsdb_repl_flags, a); + if (!W_ERROR_IS_OK(status)) { + break; + } + } + if (W_ERROR_EQUAL(status, WERR_TOO_MANY_SECRETS)) { + WERROR get_name_status = dsdb_attribute_drsuapi_to_ldb(ldb, schema, pfm_remote, + a, msg->elements, e); + if (W_ERROR_IS_OK(get_name_status)) { + DEBUG(0, ("Unxpectedly got secret value %s on %s from DRS server\n", + e->name, ldb_dn_get_linearized(msg->dn))); + } else { + DEBUG(0, ("Unxpectedly got secret value on %s from DRS server", + ldb_dn_get_linearized(msg->dn))); + } + } else if (!W_ERROR_IS_OK(status)) { + return status; } status = dsdb_attribute_drsuapi_to_ldb(ldb, schema, pfm_remote, diff --git a/source4/dsdb/samdb/samdb.h b/source4/dsdb/samdb/samdb.h index 324045a..0a1d90d 100644 --- a/source4/dsdb/samdb/samdb.h +++ b/source4/dsdb/samdb/samdb.h @@ -62,6 +62,7 @@ struct dsdb_control_current_partition { #define DSDB_REPL_FLAG_PRIORITISE_INCOMING 1 #define DSDB_REPL_FLAG_PARTIAL_REPLICA 2 #define DSDB_REPL_FLAG_ADD_NCNAME 4 +#define DSDB_REPL_FLAG_EXPECT_NO_SECRETS 8 #define DSDB_CONTROL_REPLICATED_UPDATE_OID "1.3.6.1.4.1.7165.4.3.3" diff --git a/source4/libnet/libnet_vampire.c b/source4/libnet/libnet_vampire.c index 1c3403f..1d649fc 100644 --- a/source4/libnet/libnet_vampire.c +++ b/source4/libnet/libnet_vampire.c @@ -553,6 +553,7 @@ NTSTATUS libnet_vampire_cb_store_chunk(void *private_data, const struct drsuapi_DsReplicaCursor2CtrEx *uptodateness_vector; struct dsdb_extended_replicated_objects *objs; uint32_t req_replica_flags; + uint32_t dsdb_repl_flags = 0; struct repsFromTo1 *s_dsa; char *tmp_dns_name; uint32_t i; @@ -679,6 +680,10 @@ NTSTATUS libnet_vampire_cb_store_chunk(void *private_data, return NT_STATUS_INTERNAL_ERROR; } + if (req_replica_flags & DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING) { + dsdb_repl_flags |= DSDB_REPL_FLAG_EXPECT_NO_SECRETS; + } + status = dsdb_replicated_objects_convert(s->ldb, schema, c->partition->nc.dn, @@ -690,7 +695,7 @@ NTSTATUS libnet_vampire_cb_store_chunk(void *private_data, s_dsa, uptodateness_vector, c->gensec_skey, - 0, + dsdb_repl_flags, s, &objs); if (!W_ERROR_IS_OK(status)) { DEBUG(0,("Failed to convert objects: %s\n", win_errstr(status))); From 11587e578224af82ed233fec0c4a78b22ed30759 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Wed, 19 Aug 2015 13:29:35 +1200 Subject: [PATCH 06/36] samba-tool drs clone-dc: Add --include-secrets option This allows the creation of domain clones that have no secrets, and so make it safer to examine databases that demonstrate issues Signed-off-by: Andrew Bartlett --- python/samba/join.py | 4 ++- python/samba/netcmd/drs.py | 5 ++-- python/samba/tests/blackbox/samba_tool_drs.py | 40 ++++++++++++++++++++++++++- 3 files changed, 45 insertions(+), 4 deletions(-) diff --git a/python/samba/join.py b/python/samba/join.py index 74c14c2..45e3e40 100644 --- a/python/samba/join.py +++ b/python/samba/join.py @@ -1206,7 +1206,7 @@ def join_DC(logger=None, server=None, creds=None, lp=None, site=None, netbios_na logger.info("Joined domain %s (SID %s) as a DC" % (ctx.domain_name, ctx.domsid)) def join_clone(logger=None, server=None, creds=None, lp=None, - targetdir=None, domain=None): + targetdir=None, domain=None, include_secrets=False): """Join as a DC.""" ctx = dc_join(logger, server, creds, lp, site=None, netbios_name=None, targetdir=targetdir, domain=domain, machinepass=None, use_ntvfs=False, dns_backend="NONE", promote_existing=False, clone_only=True) @@ -1222,6 +1222,8 @@ def join_clone(logger=None, server=None, creds=None, lp=None, drsuapi.DRSUAPI_DRS_PER_SYNC | drsuapi.DRSUAPI_DRS_FULL_SYNC_IN_PROGRESS | drsuapi.DRSUAPI_DRS_NEVER_SYNCED) + if not include_secrets: + ctx.replica_flags |= drsuapi.DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING ctx.domain_replica_flags = ctx.replica_flags ctx.do_join() diff --git a/python/samba/netcmd/drs.py b/python/samba/netcmd/drs.py index f1d4970..c624357 100644 --- a/python/samba/netcmd/drs.py +++ b/python/samba/netcmd/drs.py @@ -530,6 +530,7 @@ class cmd_drs_clone_dc_database(Command): Option("--server", help="DC to join", type=str), Option("--targetdir", help="where to store provision", type=str), Option("--quiet", help="Be quiet", action="store_true"), + Option("--include-secrets", help="Also replicate secret values", action="store_true"), Option("--verbose", help="Be verbose", action="store_true") ] @@ -537,7 +538,7 @@ class cmd_drs_clone_dc_database(Command): def run(self, domain, sambaopts=None, credopts=None, versionopts=None, server=None, targetdir=None, - quiet=False, verbose=False): + quiet=False, verbose=False, include_secrets=False): lp = sambaopts.get_loadparm() creds = credopts.get_credentials(lp) @@ -550,7 +551,7 @@ def run(self, domain, sambaopts=None, credopts=None, logger.setLevel(logging.INFO) join_clone(logger=logger, server=server, creds=creds, lp=lp, domain=domain, - targetdir=targetdir) + targetdir=targetdir, include_secrets=include_secrets) class cmd_drs(SuperCommand): diff --git a/python/samba/tests/blackbox/samba_tool_drs.py b/python/samba/tests/blackbox/samba_tool_drs.py index a7bcb4f..05fddac 100644 --- a/python/samba/tests/blackbox/samba_tool_drs.py +++ b/python/samba/tests/blackbox/samba_tool_drs.py @@ -123,7 +123,45 @@ def test_samba_tool_drs_clone_dc(self): # The clone should pretend to be the source server self.assertEqual(ds_name, server_ds_name) self.assertEqual(ldap_service_name, server_ldap_service_name) + + samdb = samba.tests.connect_samdb("tdb://" + os.path.join(self.tempdir, "private", "sam.ldb"), + ldap_only=False, lp=self.get_loadparm()) + def get_krbtgt_pw(): + krbtgt_pw = samdb.searchone("unicodePwd", "cn=krbtgt,CN=users,%s" % nc_name) + self.assertRaises(KeyError, get_krbtgt_pw) + shutil.rmtree(os.path.join(self.tempdir, "private")) + shutil.rmtree(os.path.join(self.tempdir, "etc")) + shutil.rmtree(os.path.join(self.tempdir, "msg.lock")) + shutil.rmtree(os.path.join(self.tempdir, "state")) + + def test_samba_tool_drs_clone_dc_secrets(self): + """Tests 'samba-tool drs clone-dc-database --include-secrets' command .""" + server_rootdse = self._get_rootDSE(self.dc1) + server_nc_name = server_rootdse["defaultNamingContext"] + server_ds_name = server_rootdse["dsServiceName"] + server_ldap_service_name = str(server_rootdse["ldapServiceName"][0]) + server_realm = server_ldap_service_name.split(":")[0] + creds = self.get_credentials() + out = self.check_output("samba-tool drs clone-dc-database %s --server=%s %s --targetdir=%s --include-secrets" + % (server_realm, + self.dc1, + self.cmdline_creds, + self.tempdir)) + ldb_rootdse = self._get_rootDSE("tdb://" + os.path.join(self.tempdir, "private", "sam.ldb"), ldap_only=False) + nc_name = ldb_rootdse["defaultNamingContext"] + ds_name = ldb_rootdse["dsServiceName"] + ldap_service_name = str(server_rootdse["ldapServiceName"][0]) + + samdb = samba.tests.connect_samdb("tdb://" + os.path.join(self.tempdir, "private", "sam.ldb"), + ldap_only=False, lp=self.get_loadparm()) + krbtgt_pw = samdb.searchone("unicodePwd", "cn=krbtgt,CN=users,%s" % nc_name) + self.assertIsNotNone(krbtgt_pw) + + self.assertEqual(nc_name, server_nc_name) + # The clone should pretend to be the source server + self.assertEqual(ds_name, server_ds_name) + self.assertEqual(ldap_service_name, server_ldap_service_name) shutil.rmtree(os.path.join(self.tempdir, "private")) shutil.rmtree(os.path.join(self.tempdir, "etc")) - shutil.rmtree(os.path.join(self.tempdir, "msg")) + shutil.rmtree(os.path.join(self.tempdir, "msg.lock")) shutil.rmtree(os.path.join(self.tempdir, "state")) From 01efd7d5578fa08c89e402d5e1e210c2f85d2ce1 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Wed, 19 Aug 2015 13:30:55 +1200 Subject: [PATCH 07/36] repl: Use DSDB_REPL_FLAG_PRIORITISE_INCOMING in samba-tool drs repliate --local Previously this would only be set when we did server-to-server replication Signed-off-by: Andrew Bartlett --- source4/libnet/libnet_vampire.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source4/libnet/libnet_vampire.c b/source4/libnet/libnet_vampire.c index 1d649fc..97c6d1a 100644 --- a/source4/libnet/libnet_vampire.c +++ b/source4/libnet/libnet_vampire.c @@ -680,6 +680,10 @@ NTSTATUS libnet_vampire_cb_store_chunk(void *private_data, return NT_STATUS_INTERNAL_ERROR; } + if (req_replica_flags & DRSUAPI_DRS_FULL_SYNC_IN_PROGRESS) { + dsdb_repl_flags |= DSDB_REPL_FLAG_PRIORITISE_INCOMING; + } + if (req_replica_flags & DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING) { dsdb_repl_flags |= DSDB_REPL_FLAG_EXPECT_NO_SECRETS; } From 79802c504405c93d7097de80074aa0066fd1994a Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Mon, 12 Oct 2015 17:50:27 +1300 Subject: [PATCH 08/36] samba-tool drs clone-dc-database: Require --targetdir Signed-off-by: Andrew Bartlett --- python/samba/netcmd/drs.py | 6 +++++- python/samba/tests/blackbox/samba_tool_drs.py | 13 +++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/python/samba/netcmd/drs.py b/python/samba/netcmd/drs.py index c624357..230dd33 100644 --- a/python/samba/netcmd/drs.py +++ b/python/samba/netcmd/drs.py @@ -528,7 +528,7 @@ class cmd_drs_clone_dc_database(Command): takes_options = [ Option("--server", help="DC to join", type=str), - Option("--targetdir", help="where to store provision", type=str), + Option("--targetdir", help="where to store provision (required)", type=str), Option("--quiet", help="Be quiet", action="store_true"), Option("--include-secrets", help="Also replicate secret values", action="store_true"), Option("--verbose", help="Be verbose", action="store_true") @@ -550,6 +550,10 @@ def run(self, domain, sambaopts=None, credopts=None, else: logger.setLevel(logging.INFO) + if targetdir is None: + raise CommandError("--targetdir option must be specified") + + join_clone(logger=logger, server=server, creds=creds, lp=lp, domain=domain, targetdir=targetdir, include_secrets=include_secrets) diff --git a/python/samba/tests/blackbox/samba_tool_drs.py b/python/samba/tests/blackbox/samba_tool_drs.py index 05fddac..1cc2b50 100644 --- a/python/samba/tests/blackbox/samba_tool_drs.py +++ b/python/samba/tests/blackbox/samba_tool_drs.py @@ -165,3 +165,16 @@ def test_samba_tool_drs_clone_dc_secrets(self): shutil.rmtree(os.path.join(self.tempdir, "etc")) shutil.rmtree(os.path.join(self.tempdir, "msg.lock")) shutil.rmtree(os.path.join(self.tempdir, "state")) + + def test_samba_tool_drs_clone_dc_secrets_without_targetdir(self): + """Tests 'samba-tool drs clone-dc-database' command without --targetdir.""" + server_rootdse = self._get_rootDSE(self.dc1) + server_ldap_service_name = str(server_rootdse["ldapServiceName"][0]) + server_realm = server_ldap_service_name.split(":")[0] + creds = self.get_credentials() + def attempt_clone(): + out = self.check_output("samba-tool drs clone-dc-database %s --server=%s %s" + % (server_realm, + self.dc1, + self.cmdline_creds)) + self.assertRaises(samba.tests.BlackboxProcessError, attempt_clone) From eb6fc6deb125770d4d1b28579e1b7017daa4d9a7 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Wed, 14 Oct 2015 13:49:01 +1300 Subject: [PATCH 09/36] ldb: Fix python bindings to accept a string as a DN This fixes add_base(), add_child() and is_child_of(). This removes a toally incorrect cast of struct ldb_dn to struct ldb_context. A helper routine is used instead Signed-off-by: Andrew Bartlett --- lib/ldb/ABI/ldb-1.1.22.sigs | 264 +++++++++++++++++++++++++++++++++++++ lib/ldb/ABI/pyldb-util-1.1.22.sigs | 2 + lib/ldb/common/ldb_dn.c | 5 + lib/ldb/include/ldb_private.h | 8 ++ lib/ldb/pyldb.c | 8 +- lib/ldb/tests/python/api.py | 29 ++++ lib/ldb/wscript | 2 +- 7 files changed, 312 insertions(+), 6 deletions(-) create mode 100644 lib/ldb/ABI/ldb-1.1.22.sigs create mode 100644 lib/ldb/ABI/pyldb-util-1.1.22.sigs diff --git a/lib/ldb/ABI/ldb-1.1.22.sigs b/lib/ldb/ABI/ldb-1.1.22.sigs new file mode 100644 index 0000000..6d9767b --- /dev/null +++ b/lib/ldb/ABI/ldb-1.1.22.sigs @@ -0,0 +1,264 @@ +ldb_add: int (struct ldb_context *, const struct ldb_message *) +ldb_any_comparison: int (struct ldb_context *, void *, ldb_attr_handler_t, const struct ldb_val *, const struct ldb_val *) +ldb_asprintf_errstring: void (struct ldb_context *, const char *, ...) +ldb_attr_casefold: char *(TALLOC_CTX *, const char *) +ldb_attr_dn: int (const char *) +ldb_attr_in_list: int (const char * const *, const char *) +ldb_attr_list_copy: const char **(TALLOC_CTX *, const char * const *) +ldb_attr_list_copy_add: const char **(TALLOC_CTX *, const char * const *, const char *) +ldb_base64_decode: int (char *) +ldb_base64_encode: char *(TALLOC_CTX *, const char *, int) +ldb_binary_decode: struct ldb_val (TALLOC_CTX *, const char *) +ldb_binary_encode: char *(TALLOC_CTX *, struct ldb_val) +ldb_binary_encode_string: char *(TALLOC_CTX *, const char *) +ldb_build_add_req: int (struct ldb_request **, struct ldb_context *, TALLOC_CTX *, const struct ldb_message *, struct ldb_control **, void *, ldb_request_callback_t, struct ldb_request *) +ldb_build_del_req: int (struct ldb_request **, struct ldb_context *, TALLOC_CTX *, struct ldb_dn *, struct ldb_control **, void *, ldb_request_callback_t, struct ldb_request *) +ldb_build_extended_req: int (struct ldb_request **, struct ldb_context *, TALLOC_CTX *, const char *, void *, struct ldb_control **, void *, ldb_request_callback_t, struct ldb_request *) +ldb_build_mod_req: int (struct ldb_request **, struct ldb_context *, TALLOC_CTX *, const struct ldb_message *, struct ldb_control **, void *, ldb_request_callback_t, struct ldb_request *) +ldb_build_rename_req: int (struct ldb_request **, struct ldb_context *, TALLOC_CTX *, struct ldb_dn *, struct ldb_dn *, struct ldb_control **, void *, ldb_request_callback_t, struct ldb_request *) +ldb_build_search_req: int (struct ldb_request **, struct ldb_context *, TALLOC_CTX *, struct ldb_dn *, enum ldb_scope, const char *, const char * const *, struct ldb_control **, void *, ldb_request_callback_t, struct ldb_request *) +ldb_build_search_req_ex: int (struct ldb_request **, struct ldb_context *, TALLOC_CTX *, struct ldb_dn *, enum ldb_scope, struct ldb_parse_tree *, const char * const *, struct ldb_control **, void *, ldb_request_callback_t, struct ldb_request *) +ldb_casefold: char *(struct ldb_context *, TALLOC_CTX *, const char *, size_t) +ldb_casefold_default: char *(void *, TALLOC_CTX *, const char *, size_t) +ldb_check_critical_controls: int (struct ldb_control **) +ldb_comparison_binary: int (struct ldb_context *, void *, const struct ldb_val *, const struct ldb_val *) +ldb_comparison_fold: int (struct ldb_context *, void *, const struct ldb_val *, const struct ldb_val *) +ldb_connect: int (struct ldb_context *, const char *, unsigned int, const char **) +ldb_control_to_string: char *(TALLOC_CTX *, const struct ldb_control *) +ldb_controls_except_specified: struct ldb_control **(struct ldb_control **, TALLOC_CTX *, struct ldb_control *) +ldb_debug: void (struct ldb_context *, enum ldb_debug_level, const char *, ...) +ldb_debug_add: void (struct ldb_context *, const char *, ...) +ldb_debug_end: void (struct ldb_context *, enum ldb_debug_level) +ldb_debug_set: void (struct ldb_context *, enum ldb_debug_level, const char *, ...) +ldb_delete: int (struct ldb_context *, struct ldb_dn *) +ldb_dn_add_base: bool (struct ldb_dn *, struct ldb_dn *) +ldb_dn_add_base_fmt: bool (struct ldb_dn *, const char *, ...) +ldb_dn_add_child: bool (struct ldb_dn *, struct ldb_dn *) +ldb_dn_add_child_fmt: bool (struct ldb_dn *, const char *, ...) +ldb_dn_alloc_casefold: char *(TALLOC_CTX *, struct ldb_dn *) +ldb_dn_alloc_linearized: char *(TALLOC_CTX *, struct ldb_dn *) +ldb_dn_canonical_ex_string: char *(TALLOC_CTX *, struct ldb_dn *) +ldb_dn_canonical_string: char *(TALLOC_CTX *, struct ldb_dn *) +ldb_dn_check_local: bool (struct ldb_module *, struct ldb_dn *) +ldb_dn_check_special: bool (struct ldb_dn *, const char *) +ldb_dn_compare: int (struct ldb_dn *, struct ldb_dn *) +ldb_dn_compare_base: int (struct ldb_dn *, struct ldb_dn *) +ldb_dn_copy: struct ldb_dn *(TALLOC_CTX *, struct ldb_dn *) +ldb_dn_escape_value: char *(TALLOC_CTX *, struct ldb_val) +ldb_dn_extended_add_syntax: int (struct ldb_context *, unsigned int, const struct ldb_dn_extended_syntax *) +ldb_dn_extended_filter: void (struct ldb_dn *, const char * const *) +ldb_dn_extended_syntax_by_name: const struct ldb_dn_extended_syntax *(struct ldb_context *, const char *) +ldb_dn_from_ldb_val: struct ldb_dn *(TALLOC_CTX *, struct ldb_context *, const struct ldb_val *) +ldb_dn_get_casefold: const char *(struct ldb_dn *) +ldb_dn_get_comp_num: int (struct ldb_dn *) +ldb_dn_get_component_name: const char *(struct ldb_dn *, unsigned int) +ldb_dn_get_component_val: const struct ldb_val *(struct ldb_dn *, unsigned int) +ldb_dn_get_extended_comp_num: int (struct ldb_dn *) +ldb_dn_get_extended_component: const struct ldb_val *(struct ldb_dn *, const char *) +ldb_dn_get_extended_linearized: char *(TALLOC_CTX *, struct ldb_dn *, int) +ldb_dn_get_ldb_context: struct ldb_context *(struct ldb_dn *) +ldb_dn_get_linearized: const char *(struct ldb_dn *) +ldb_dn_get_parent: struct ldb_dn *(TALLOC_CTX *, struct ldb_dn *) +ldb_dn_get_rdn_name: const char *(struct ldb_dn *) +ldb_dn_get_rdn_val: const struct ldb_val *(struct ldb_dn *) +ldb_dn_has_extended: bool (struct ldb_dn *) +ldb_dn_is_null: bool (struct ldb_dn *) +ldb_dn_is_special: bool (struct ldb_dn *) +ldb_dn_is_valid: bool (struct ldb_dn *) +ldb_dn_map_local: struct ldb_dn *(struct ldb_module *, void *, struct ldb_dn *) +ldb_dn_map_rebase_remote: struct ldb_dn *(struct ldb_module *, void *, struct ldb_dn *) +ldb_dn_map_remote: struct ldb_dn *(struct ldb_module *, void *, struct ldb_dn *) +ldb_dn_minimise: bool (struct ldb_dn *) +ldb_dn_new: struct ldb_dn *(TALLOC_CTX *, struct ldb_context *, const char *) +ldb_dn_new_fmt: struct ldb_dn *(TALLOC_CTX *, struct ldb_context *, const char *, ...) +ldb_dn_remove_base_components: bool (struct ldb_dn *, unsigned int) +ldb_dn_remove_child_components: bool (struct ldb_dn *, unsigned int) +ldb_dn_remove_extended_components: void (struct ldb_dn *) +ldb_dn_replace_components: bool (struct ldb_dn *, struct ldb_dn *) +ldb_dn_set_component: int (struct ldb_dn *, int, const char *, const struct ldb_val) +ldb_dn_set_extended_component: int (struct ldb_dn *, const char *, const struct ldb_val *) +ldb_dn_update_components: int (struct ldb_dn *, const struct ldb_dn *) +ldb_dn_validate: bool (struct ldb_dn *) +ldb_dump_results: void (struct ldb_context *, struct ldb_result *, FILE *) +ldb_error_at: int (struct ldb_context *, int, const char *, const char *, int) +ldb_errstring: const char *(struct ldb_context *) +ldb_extended: int (struct ldb_context *, const char *, void *, struct ldb_result **) +ldb_extended_default_callback: int (struct ldb_request *, struct ldb_reply *) +ldb_filter_from_tree: char *(TALLOC_CTX *, const struct ldb_parse_tree *) +ldb_get_config_basedn: struct ldb_dn *(struct ldb_context *) +ldb_get_create_perms: unsigned int (struct ldb_context *) +ldb_get_default_basedn: struct ldb_dn *(struct ldb_context *) +ldb_get_event_context: struct tevent_context *(struct ldb_context *) +ldb_get_flags: unsigned int (struct ldb_context *) +ldb_get_opaque: void *(struct ldb_context *, const char *) +ldb_get_root_basedn: struct ldb_dn *(struct ldb_context *) +ldb_get_schema_basedn: struct ldb_dn *(struct ldb_context *) +ldb_global_init: int (void) +ldb_handle_new: struct ldb_handle *(TALLOC_CTX *, struct ldb_context *) +ldb_handler_copy: int (struct ldb_context *, void *, const struct ldb_val *, struct ldb_val *) +ldb_handler_fold: int (struct ldb_context *, void *, const struct ldb_val *, struct ldb_val *) +ldb_init: struct ldb_context *(TALLOC_CTX *, struct tevent_context *) +ldb_ldif_message_string: char *(struct ldb_context *, TALLOC_CTX *, enum ldb_changetype, const struct ldb_message *) +ldb_ldif_parse_modrdn: int (struct ldb_context *, const struct ldb_ldif *, TALLOC_CTX *, struct ldb_dn **, struct ldb_dn **, bool *, struct ldb_dn **, struct ldb_dn **) +ldb_ldif_read: struct ldb_ldif *(struct ldb_context *, int (*)(void *), void *) +ldb_ldif_read_file: struct ldb_ldif *(struct ldb_context *, FILE *) +ldb_ldif_read_file_state: struct ldb_ldif *(struct ldb_context *, struct ldif_read_file_state *) +ldb_ldif_read_free: void (struct ldb_context *, struct ldb_ldif *) +ldb_ldif_read_string: struct ldb_ldif *(struct ldb_context *, const char **) +ldb_ldif_write: int (struct ldb_context *, int (*)(void *, const char *, ...), void *, const struct ldb_ldif *) +ldb_ldif_write_file: int (struct ldb_context *, FILE *, const struct ldb_ldif *) +ldb_ldif_write_redacted_trace_string: char *(struct ldb_context *, TALLOC_CTX *, const struct ldb_ldif *) +ldb_ldif_write_string: char *(struct ldb_context *, TALLOC_CTX *, const struct ldb_ldif *) +ldb_load_modules: int (struct ldb_context *, const char **) +ldb_map_add: int (struct ldb_module *, struct ldb_request *) +ldb_map_delete: int (struct ldb_module *, struct ldb_request *) +ldb_map_init: int (struct ldb_module *, const struct ldb_map_attribute *, const struct ldb_map_objectclass *, const char * const *, const char *, const char *) +ldb_map_modify: int (struct ldb_module *, struct ldb_request *) +ldb_map_rename: int (struct ldb_module *, struct ldb_request *) +ldb_map_search: int (struct ldb_module *, struct ldb_request *) +ldb_match_msg: int (struct ldb_context *, const struct ldb_message *, const struct ldb_parse_tree *, struct ldb_dn *, enum ldb_scope) +ldb_match_msg_error: int (struct ldb_context *, const struct ldb_message *, const struct ldb_parse_tree *, struct ldb_dn *, enum ldb_scope, bool *) +ldb_match_msg_objectclass: int (const struct ldb_message *, const char *) +ldb_mod_register_control: int (struct ldb_module *, const char *) +ldb_modify: int (struct ldb_context *, const struct ldb_message *) +ldb_modify_default_callback: int (struct ldb_request *, struct ldb_reply *) +ldb_module_call_chain: char *(struct ldb_request *, TALLOC_CTX *) +ldb_module_connect_backend: int (struct ldb_context *, const char *, const char **, struct ldb_module **) +ldb_module_done: int (struct ldb_request *, struct ldb_control **, struct ldb_extended *, int) +ldb_module_flags: uint32_t (struct ldb_context *) +ldb_module_get_ctx: struct ldb_context *(struct ldb_module *) +ldb_module_get_name: const char *(struct ldb_module *) +ldb_module_get_ops: const struct ldb_module_ops *(struct ldb_module *) +ldb_module_get_private: void *(struct ldb_module *) +ldb_module_init_chain: int (struct ldb_context *, struct ldb_module *) +ldb_module_load_list: int (struct ldb_context *, const char **, struct ldb_module *, struct ldb_module **) +ldb_module_new: struct ldb_module *(TALLOC_CTX *, struct ldb_context *, const char *, const struct ldb_module_ops *) +ldb_module_next: struct ldb_module *(struct ldb_module *) +ldb_module_popt_options: struct poptOption **(struct ldb_context *) +ldb_module_send_entry: int (struct ldb_request *, struct ldb_message *, struct ldb_control **) +ldb_module_send_referral: int (struct ldb_request *, char *) +ldb_module_set_next: void (struct ldb_module *, struct ldb_module *) +ldb_module_set_private: void (struct ldb_module *, void *) +ldb_modules_hook: int (struct ldb_context *, enum ldb_module_hook_type) +ldb_modules_list_from_string: const char **(struct ldb_context *, TALLOC_CTX *, const char *) +ldb_modules_load: int (const char *, const char *) +ldb_msg_add: int (struct ldb_message *, const struct ldb_message_element *, int) +ldb_msg_add_empty: int (struct ldb_message *, const char *, int, struct ldb_message_element **) +ldb_msg_add_fmt: int (struct ldb_message *, const char *, const char *, ...) +ldb_msg_add_linearized_dn: int (struct ldb_message *, const char *, struct ldb_dn *) +ldb_msg_add_steal_string: int (struct ldb_message *, const char *, char *) +ldb_msg_add_steal_value: int (struct ldb_message *, const char *, struct ldb_val *) +ldb_msg_add_string: int (struct ldb_message *, const char *, const char *) +ldb_msg_add_value: int (struct ldb_message *, const char *, const struct ldb_val *, struct ldb_message_element **) +ldb_msg_canonicalize: struct ldb_message *(struct ldb_context *, const struct ldb_message *) +ldb_msg_check_string_attribute: int (const struct ldb_message *, const char *, const char *) +ldb_msg_copy: struct ldb_message *(TALLOC_CTX *, const struct ldb_message *) +ldb_msg_copy_attr: int (struct ldb_message *, const char *, const char *) +ldb_msg_copy_shallow: struct ldb_message *(TALLOC_CTX *, const struct ldb_message *) +ldb_msg_diff: struct ldb_message *(struct ldb_context *, struct ldb_message *, struct ldb_message *) +ldb_msg_difference: int (struct ldb_context *, TALLOC_CTX *, struct ldb_message *, struct ldb_message *, struct ldb_message **) +ldb_msg_element_compare: int (struct ldb_message_element *, struct ldb_message_element *) +ldb_msg_element_compare_name: int (struct ldb_message_element *, struct ldb_message_element *) +ldb_msg_element_equal_ordered: bool (const struct ldb_message_element *, const struct ldb_message_element *) +ldb_msg_find_attr_as_bool: int (const struct ldb_message *, const char *, int) +ldb_msg_find_attr_as_dn: struct ldb_dn *(struct ldb_context *, TALLOC_CTX *, const struct ldb_message *, const char *) +ldb_msg_find_attr_as_double: double (const struct ldb_message *, const char *, double) +ldb_msg_find_attr_as_int: int (const struct ldb_message *, const char *, int) +ldb_msg_find_attr_as_int64: int64_t (const struct ldb_message *, const char *, int64_t) +ldb_msg_find_attr_as_string: const char *(const struct ldb_message *, const char *, const char *) +ldb_msg_find_attr_as_uint: unsigned int (const struct ldb_message *, const char *, unsigned int) +ldb_msg_find_attr_as_uint64: uint64_t (const struct ldb_message *, const char *, uint64_t) +ldb_msg_find_element: struct ldb_message_element *(const struct ldb_message *, const char *) +ldb_msg_find_ldb_val: const struct ldb_val *(const struct ldb_message *, const char *) +ldb_msg_find_val: struct ldb_val *(const struct ldb_message_element *, struct ldb_val *) +ldb_msg_new: struct ldb_message *(TALLOC_CTX *) +ldb_msg_normalize: int (struct ldb_context *, TALLOC_CTX *, const struct ldb_message *, struct ldb_message **) +ldb_msg_remove_attr: void (struct ldb_message *, const char *) +ldb_msg_remove_element: void (struct ldb_message *, struct ldb_message_element *) +ldb_msg_rename_attr: int (struct ldb_message *, const char *, const char *) +ldb_msg_sanity_check: int (struct ldb_context *, const struct ldb_message *) +ldb_msg_sort_elements: void (struct ldb_message *) +ldb_next_del_trans: int (struct ldb_module *) +ldb_next_end_trans: int (struct ldb_module *) +ldb_next_init: int (struct ldb_module *) +ldb_next_prepare_commit: int (struct ldb_module *) +ldb_next_remote_request: int (struct ldb_module *, struct ldb_request *) +ldb_next_request: int (struct ldb_module *, struct ldb_request *) +ldb_next_start_trans: int (struct ldb_module *) +ldb_op_default_callback: int (struct ldb_request *, struct ldb_reply *) +ldb_options_find: const char *(struct ldb_context *, const char **, const char *) +ldb_pack_data: int (struct ldb_context *, const struct ldb_message *, struct ldb_val *) +ldb_parse_control_from_string: struct ldb_control *(struct ldb_context *, TALLOC_CTX *, const char *) +ldb_parse_control_strings: struct ldb_control **(struct ldb_context *, TALLOC_CTX *, const char **) +ldb_parse_tree: struct ldb_parse_tree *(TALLOC_CTX *, const char *) +ldb_parse_tree_attr_replace: void (struct ldb_parse_tree *, const char *, const char *) +ldb_parse_tree_copy_shallow: struct ldb_parse_tree *(TALLOC_CTX *, const struct ldb_parse_tree *) +ldb_parse_tree_walk: int (struct ldb_parse_tree *, int (*)(struct ldb_parse_tree *, void *), void *) +ldb_qsort: void (void * const, size_t, size_t, void *, ldb_qsort_cmp_fn_t) +ldb_register_backend: int (const char *, ldb_connect_fn, bool) +ldb_register_extended_match_rule: int (struct ldb_context *, const struct ldb_extended_match_rule *) +ldb_register_hook: int (ldb_hook_fn) +ldb_register_module: int (const struct ldb_module_ops *) +ldb_rename: int (struct ldb_context *, struct ldb_dn *, struct ldb_dn *) +ldb_reply_add_control: int (struct ldb_reply *, const char *, bool, void *) +ldb_reply_get_control: struct ldb_control *(struct ldb_reply *, const char *) +ldb_req_get_custom_flags: uint32_t (struct ldb_request *) +ldb_req_is_untrusted: bool (struct ldb_request *) +ldb_req_location: const char *(struct ldb_request *) +ldb_req_mark_trusted: void (struct ldb_request *) +ldb_req_mark_untrusted: void (struct ldb_request *) +ldb_req_set_custom_flags: void (struct ldb_request *, uint32_t) +ldb_req_set_location: void (struct ldb_request *, const char *) +ldb_request: int (struct ldb_context *, struct ldb_request *) +ldb_request_add_control: int (struct ldb_request *, const char *, bool, void *) +ldb_request_done: int (struct ldb_request *, int) +ldb_request_get_control: struct ldb_control *(struct ldb_request *, const char *) +ldb_request_get_status: int (struct ldb_request *) +ldb_request_replace_control: int (struct ldb_request *, const char *, bool, void *) +ldb_request_set_state: void (struct ldb_request *, int) +ldb_reset_err_string: void (struct ldb_context *) +ldb_save_controls: int (struct ldb_control *, struct ldb_request *, struct ldb_control ***) +ldb_schema_attribute_add: int (struct ldb_context *, const char *, unsigned int, const char *) +ldb_schema_attribute_add_with_syntax: int (struct ldb_context *, const char *, unsigned int, const struct ldb_schema_syntax *) +ldb_schema_attribute_by_name: const struct ldb_schema_attribute *(struct ldb_context *, const char *) +ldb_schema_attribute_remove: void (struct ldb_context *, const char *) +ldb_schema_attribute_set_override_handler: void (struct ldb_context *, ldb_attribute_handler_override_fn_t, void *) +ldb_search: int (struct ldb_context *, TALLOC_CTX *, struct ldb_result **, struct ldb_dn *, enum ldb_scope, const char * const *, const char *, ...) +ldb_search_default_callback: int (struct ldb_request *, struct ldb_reply *) +ldb_sequence_number: int (struct ldb_context *, enum ldb_sequence_type, uint64_t *) +ldb_set_create_perms: void (struct ldb_context *, unsigned int) +ldb_set_debug: int (struct ldb_context *, void (*)(void *, enum ldb_debug_level, const char *, va_list), void *) +ldb_set_debug_stderr: int (struct ldb_context *) +ldb_set_default_dns: void (struct ldb_context *) +ldb_set_errstring: void (struct ldb_context *, const char *) +ldb_set_event_context: void (struct ldb_context *, struct tevent_context *) +ldb_set_flags: void (struct ldb_context *, unsigned int) +ldb_set_modules_dir: void (struct ldb_context *, const char *) +ldb_set_opaque: int (struct ldb_context *, const char *, void *) +ldb_set_timeout: int (struct ldb_context *, struct ldb_request *, int) +ldb_set_timeout_from_prev_req: int (struct ldb_context *, struct ldb_request *, struct ldb_request *) +ldb_set_utf8_default: void (struct ldb_context *) +ldb_set_utf8_fns: void (struct ldb_context *, void *, char *(*)(void *, void *, const char *, size_t)) +ldb_setup_wellknown_attributes: int (struct ldb_context *) +ldb_should_b64_encode: int (struct ldb_context *, const struct ldb_val *) +ldb_standard_syntax_by_name: const struct ldb_schema_syntax *(struct ldb_context *, const char *) +ldb_strerror: const char *(int) +ldb_string_to_time: time_t (const char *) +ldb_string_utc_to_time: time_t (const char *) +ldb_timestring: char *(TALLOC_CTX *, time_t) +ldb_timestring_utc: char *(TALLOC_CTX *, time_t) +ldb_transaction_cancel: int (struct ldb_context *) +ldb_transaction_cancel_noerr: int (struct ldb_context *) +ldb_transaction_commit: int (struct ldb_context *) +ldb_transaction_prepare_commit: int (struct ldb_context *) +ldb_transaction_start: int (struct ldb_context *) +ldb_unpack_data: int (struct ldb_context *, const struct ldb_val *, struct ldb_message *) +ldb_val_dup: struct ldb_val (TALLOC_CTX *, const struct ldb_val *) +ldb_val_equal_exact: int (const struct ldb_val *, const struct ldb_val *) +ldb_val_map_local: struct ldb_val (struct ldb_module *, void *, const struct ldb_map_attribute *, const struct ldb_val *) +ldb_val_map_remote: struct ldb_val (struct ldb_module *, void *, const struct ldb_map_attribute *, const struct ldb_val *) +ldb_val_string_cmp: int (const struct ldb_val *, const char *) +ldb_val_to_time: int (const struct ldb_val *, time_t *) +ldb_valid_attr_name: int (const char *) +ldb_vdebug: void (struct ldb_context *, enum ldb_debug_level, const char *, va_list) +ldb_wait: int (struct ldb_handle *, enum ldb_wait_type) diff --git a/lib/ldb/ABI/pyldb-util-1.1.22.sigs b/lib/ldb/ABI/pyldb-util-1.1.22.sigs new file mode 100644 index 0000000..74d6719 --- /dev/null +++ b/lib/ldb/ABI/pyldb-util-1.1.22.sigs @@ -0,0 +1,2 @@ +pyldb_Dn_FromDn: PyObject *(struct ldb_dn *) +pyldb_Object_AsDn: bool (TALLOC_CTX *, PyObject *, struct ldb_context *, struct ldb_dn **) diff --git a/lib/ldb/common/ldb_dn.c b/lib/ldb/common/ldb_dn.c index 6b6f90c..85f89c1b 100644 --- a/lib/ldb/common/ldb_dn.c +++ b/lib/ldb/common/ldb_dn.c @@ -2155,3 +2155,8 @@ bool ldb_dn_minimise(struct ldb_dn *dn) return true; } + +struct ldb_context *ldb_dn_get_ldb_context(struct ldb_dn *dn) +{ + return dn->ldb; +} diff --git a/lib/ldb/include/ldb_private.h b/lib/ldb/include/ldb_private.h index 7977448..eef5e1b 100644 --- a/lib/ldb/include/ldb_private.h +++ b/lib/ldb/include/ldb_private.h @@ -211,4 +211,12 @@ int ldb_unpack_data(struct ldb_context *ldb, const struct ldb_val *data, struct ldb_message *message); +/* + * Get the LDB context in use on an LDB DN. + * + * This is helpful to the python LDB code, which may use as part of + * adding base and child components to an existing DN. + */ +struct ldb_context *ldb_dn_get_ldb_context(struct ldb_dn *dn); + #endif diff --git a/lib/ldb/pyldb.c b/lib/ldb/pyldb.c index f18e06e..307f426 100644 --- a/lib/ldb/pyldb.c +++ b/lib/ldb/pyldb.c @@ -497,8 +497,6 @@ static PyObject *py_ldb_dn_get_parent(PyLdbDnObject *self) return (PyObject *)py_ret; } -#define dn_ldb_ctx(dn) ((struct ldb_context *)dn) - static PyObject *py_ldb_dn_add_child(PyLdbDnObject *self, PyObject *args) { PyObject *py_other; @@ -508,7 +506,7 @@ static PyObject *py_ldb_dn_add_child(PyLdbDnObject *self, PyObject *args) dn = pyldb_Dn_AsDn((PyObject *)self); - if (!pyldb_Object_AsDn(NULL, py_other, dn_ldb_ctx(dn), &other)) + if (!pyldb_Object_AsDn(NULL, py_other, ldb_dn_get_ldb_context(dn), &other)) return NULL; return PyBool_FromLong(ldb_dn_add_child(dn, other)); @@ -523,7 +521,7 @@ static PyObject *py_ldb_dn_add_base(PyLdbDnObject *self, PyObject *args) dn = pyldb_Dn_AsDn((PyObject *)self); - if (!pyldb_Object_AsDn(NULL, py_other, dn_ldb_ctx(dn), &other)) + if (!pyldb_Object_AsDn(NULL, py_other, ldb_dn_get_ldb_context(dn), &other)) return NULL; return PyBool_FromLong(ldb_dn_add_base(dn, other)); @@ -550,7 +548,7 @@ static PyObject *py_ldb_dn_is_child_of(PyLdbDnObject *self, PyObject *args) dn = pyldb_Dn_AsDn((PyObject *)self); - if (!pyldb_Object_AsDn(NULL, py_base, dn_ldb_ctx(dn), &base)) + if (!pyldb_Object_AsDn(NULL, py_base, ldb_dn_get_ldb_context(dn), &base)) return NULL; return PyBool_FromLong(ldb_dn_compare_base(base, dn) == 0); diff --git a/lib/ldb/tests/python/api.py b/lib/ldb/tests/python/api.py index d101de8..87fa797 100755 --- a/lib/ldb/tests/python/api.py +++ b/lib/ldb/tests/python/api.py @@ -411,6 +411,17 @@ def test_add_base(self): self.assertTrue(x.add_base(base)) self.assertEquals("dc=foo23,bar=bloe,bla=bloe", x.__str__()) + def test_add_child_str(self): + x = ldb.Dn(self.ldb, "dc=foo22,bar=bloe") + self.assertTrue(x.add_child("bla=bloe")) + self.assertEquals("bla=bloe,dc=foo22,bar=bloe", x.__str__()) + + def test_add_base_str(self): + x = ldb.Dn(self.ldb, "dc=foo23,bar=bloe") + base = "bla=bloe" + self.assertTrue(x.add_base(base)) + self.assertEquals("dc=foo23,bar=bloe,bla=bloe", x.__str__()) + def test_add(self): x = ldb.Dn(self.ldb, "dc=foo24") y = ldb.Dn(self.ldb, "bar=bla") @@ -457,6 +468,24 @@ def test_ldb_is_child_of(self): self.assertFalse(dn3.is_child_of(dn2)) self.assertFalse(dn1.is_child_of(dn4)) + def test_ldb_is_child_of_str(self): + """Testing ldb_dn_compare_dn""" + dn1_str = "dc=base" + dn2_str = "cn=foo,dc=base" + dn3_str = "cn=bar,dc=base" + dn4_str = "cn=baz,cn=bar,dc=base" + + dn1 = ldb.Dn(self.ldb, dn1_str) + dn2 = ldb.Dn(self.ldb, dn2_str) + dn3 = ldb.Dn(self.ldb, dn3_str) + dn4 = ldb.Dn(self.ldb, dn4_str) + + self.assertTrue(dn2.is_child_of(dn1_str)) + self.assertTrue(dn4.is_child_of(dn1_str)) + self.assertTrue(dn4.is_child_of(dn3_str)) + self.assertFalse(dn3.is_child_of(dn2_str)) + self.assertFalse(dn1.is_child_of(dn4_str)) + class LdbMsgTests(TestCase): def setUp(self): diff --git a/lib/ldb/wscript b/lib/ldb/wscript index 0996f51..70b84cb 100755 --- a/lib/ldb/wscript +++ b/lib/ldb/wscript @@ -1,7 +1,7 @@ #!/usr/bin/env python APPNAME = 'ldb' -VERSION = '1.1.21' +VERSION = '1.1.22' blddir = 'bin' From e69ca75529e629d80f2df25d56842db2d4c000c2 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Mon, 14 Sep 2015 12:12:31 +1200 Subject: [PATCH 10/36] python: Extend the samba_kcc --exportldif to include the DC account This will allow us to write (non-)KCC tests that need more of the database. In particular, it will allow testing of DC removal. Signed-off-by: Andrew Bartlett --- python/samba/kcc/ldif_import_export.py | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/python/samba/kcc/ldif_import_export.py b/python/samba/kcc/ldif_import_export.py index ab7c7a0..ca2dc47 100644 --- a/python/samba/kcc/ldif_import_export.py +++ b/python/samba/kcc/ldif_import_export.py @@ -342,7 +342,8 @@ def samdb_to_ldif_file(samdb, dburl, lp, creds, ldif_file): "whenChanged", "systemFlags", "dNSHostName", - "mailAddress"] + "mailAddress", + "serverReference"] sstr = "CN=Sites,%s" % samdb.get_config_basedn() res = samdb.search(sstr, scope=ldb.SCOPE_SUBTREE, @@ -352,6 +353,27 @@ def samdb_to_ldif_file(samdb, dburl, lp, creds, ldif_file): # Write server output write_search_result(samdb, f, res) + # Query server account objects + # This is not needed for the KCC, but allows other tests and + # examinations of a real, complex network + attrs = ["objectClass", + "objectGUID", + "cn", + "whenChanged", + "systemFlags", + "dNSHostName", + "samAccountName", + "servicePrincipalName", + "rIDSetReferences"] + for server in res: + if "serverReference" in server: + basedn = server["serverReference"][0] + res2 = samdb.search(base=basedn, scope=ldb.SCOPE_SUBTREE, + attrs=attrs) + + # Write server account output + write_search_result(samdb, f, res2) + # Query Naming Context replicas attrs = ["objectClass", "objectGUID", From f4b25383185655dde228b0c6e5997fb10a15b968 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Mon, 14 Sep 2015 15:38:21 +1200 Subject: [PATCH 11/36] python: Extend the samba_kcc --exportldif to include the RODC KDC account This will allow us to write (non-)KCC tests that need more of the database. In particular, it will allow testing of full RODC removal. Signed-off-by: Andrew Bartlett --- python/samba/kcc/ldif_import_export.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/python/samba/kcc/ldif_import_export.py b/python/samba/kcc/ldif_import_export.py index ca2dc47..41442d5 100644 --- a/python/samba/kcc/ldif_import_export.py +++ b/python/samba/kcc/ldif_import_export.py @@ -364,6 +364,7 @@ def samdb_to_ldif_file(samdb, dburl, lp, creds, ldif_file): "dNSHostName", "samAccountName", "servicePrincipalName", + "msDS-KrbTgtLink", "rIDSetReferences"] for server in res: if "serverReference" in server: @@ -374,6 +375,22 @@ def samdb_to_ldif_file(samdb, dburl, lp, creds, ldif_file): # Write server account output write_search_result(samdb, f, res2) + res2 = samdb.search(base=basedn, scope=ldb.SCOPE_BASE, + attrs=attrs) + + # + # Look for an RODC KDC attached to this server + # + if "msDS-KrbTgtLink" in res2[0]: + basedn = res2[0]["msDS-KrbTgtLink"][0] + + res3 = samdb.search(base=basedn, scope=ldb.SCOPE_SUBTREE, + attrs=attrs) + + # Write kdc account output + write_search_result(samdb, f, res3) + + # Query Naming Context replicas attrs = ["objectClass", "objectGUID", From cbfe12306b550243515b1d39f8150797299a5939 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Mon, 14 Sep 2015 13:48:04 +1200 Subject: [PATCH 12/36] python/kcc: Write correct module list into the file during ldif_to_samdb Signed-off-by: Andrew Bartlett --- python/samba/kcc/ldif_import_export.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/python/samba/kcc/ldif_import_export.py b/python/samba/kcc/ldif_import_export.py index 41442d5..e13db58 100644 --- a/python/samba/kcc/ldif_import_export.py +++ b/python/samba/kcc/ldif_import_export.py @@ -70,9 +70,13 @@ def ldif_to_samdb(dburl, lp, ldif_file, forced_local_dsa=None): changetype: modify replace: dsServiceName dsServiceName: CN=NTDS Settings,%s -- """ % forced_local_dsa) + tmpdb.add_ldif("""dn: @MODULES +@LIST: rootdse,extended_dn_in,extended_dn_out_ldb +- +""") + except Exception, estr: tmpdb.transaction_cancel() raise LdifError("Failed to import %s: %s" % (ldif_file, estr)) @@ -82,9 +86,7 @@ def ldif_to_samdb(dburl, lp, ldif_file, forced_local_dsa=None): # We have an abbreviated list of options here because we have built # an abbreviated database. We use the rootdse and extended-dn # modules only during this re-open - samdb = SamDB(url=dburl, session_info=system_session(), lp=lp, - options=["modules:rootdse,extended_dn_in," - "extended_dn_out_ldb"]) + samdb = SamDB(url=dburl, session_info=system_session(), lp=lp) return samdb From 33cdda7abcf766ade5183a5488afd10d364077e7 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Mon, 14 Sep 2015 13:47:31 +1200 Subject: [PATCH 13/36] selftest: Add tests for samdb_to_ldif_file Signed-off-by: Andrew Bartlett --- python/samba/tests/kcc/ldif_import_export.py | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/python/samba/tests/kcc/ldif_import_export.py b/python/samba/tests/kcc/ldif_import_export.py index 67bcd39..f3352e2 100644 --- a/python/samba/tests/kcc/ldif_import_export.py +++ b/python/samba/tests/kcc/ldif_import_export.py @@ -127,9 +127,28 @@ def test_ldif_to_samdb_forced_local_dsa(self): self.remove_files(dburl) - def samdb_to_ldif_file(self): - #samdb_to_ldif_file(samdb, dburl, lp, creds, ldif_file): - pass + def test_samdb_to_ldif_file(self): + dburl = os.path.join(self.tempdir, "ldap") + dburl2 = os.path.join(self.tempdir, "ldap_roundtrip") + ldif_file = os.path.join(self.tempdir, "ldif") + samdb = ldif_import_export.ldif_to_samdb(dburl, self.lp, + MULTISITE_LDIF) + self.assertIsInstance(samdb, SamDB) + ldif_import_export.samdb_to_ldif_file(samdb, dburl, + lp=self.lp, creds=None, + ldif_file=ldif_file) + self.assertGreater(os.path.getsize(ldif_file), 1000, + "LDIF should be larger than 1000 bytes") + samdb = ldif_import_export.ldif_to_samdb(dburl2, self.lp, + ldif_file) + self.assertIsInstance(samdb, SamDB) + dsa = ("CN=WIN01,CN=Servers,CN=Default-First-Site-Name,CN=Sites," + "CN=Configuration,DC=ad,DC=samba,DC=example,DC=com") + res = samdb.search(ldb.Dn(samdb, "CN=NTDS Settings," + dsa), + scope=ldb.SCOPE_BASE, attrs=["objectGUID"]) + self.remove_files(dburl) + self.remove_files(dburl2) + self.remove_files(ldif_file) class KCCMultisiteLdifTests(samba.tests.TestCaseInTempDir): From 5af2dc2a792d55b1868da916dbbcd9eb2b6e3153 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Mon, 14 Sep 2015 15:06:42 +1200 Subject: [PATCH 14/36] testdata: Include more of the multi-site network in the test fixture This allows us to test removing a DC from a realistic network Signed-off-by: Andrew Bartlett --- testdata/ldif-utils-test-multisite.ldif | 826 ++++++++++++++++++++++++++++++++ 1 file changed, 826 insertions(+) diff --git a/testdata/ldif-utils-test-multisite.ldif b/testdata/ldif-utils-test-multisite.ldif index 175b9da..fd2e1fa 100644 --- a/testdata/ldif-utils-test-multisite.ldif +++ b/testdata/ldif-utils-test-multisite.ldif @@ -857,6 +857,8 @@ cn: WIN01 objectGUID: a8ce98ac-226d-4994-977e-e97386655d97 systemFlags: 1375731712 dNSHostName: win01.ad.samba.example.com +serverReference: CN=WIN01,OU=Domain Controllers,DC=ad,DC=samba,DC=example,DC=c + om whenChanged: 20150227014848.0Z dn: CN=WIN02,CN=Servers,CN=Site-2,CN=Sites,CN=Configuration,DC=ad,DC=samba,DC=example,DC=com @@ -866,6 +868,8 @@ cn: WIN02 objectGUID: c549c41b-5936-4cd6-a144-a9036215588a systemFlags: 1375731712 dNSHostName: win02.ad.samba.example.com +serverReference: CN=WIN02,OU=Domain Controllers,DC=ad,DC=samba,DC=example,DC=c + om whenChanged: 20150227014848.0Z dn: CN=WIN03,CN=Servers,CN=Site-2,CN=Sites,CN=Configuration,DC=ad,DC=samba,DC=example,DC=com @@ -875,6 +879,8 @@ cn: WIN03 objectGUID: 939ca019-2e4e-4430-809c-9b8da640e8fc systemFlags: 1375731712 dNSHostName: win03.ad.samba.example.com +serverReference: CN=WIN03,OU=Domain Controllers,DC=ad,DC=samba,DC=example,DC=c + om whenChanged: 20150227014848.0Z dn: CN=WIN04,CN=Servers,CN=Site-2,CN=Sites,CN=Configuration,DC=ad,DC=samba,DC=example,DC=com @@ -884,6 +890,8 @@ cn: WIN04 objectGUID: 134cafa9-8058-49ed-bf55-abbf0e207d7c systemFlags: 1375731712 dNSHostName: win04.ad.samba.example.com +serverReference: CN=WIN04,OU=Domain Controllers,DC=ad,DC=samba,DC=example,DC=c + om whenChanged: 20150227014848.0Z dn: CN=WIN05,CN=Servers,CN=Site-2,CN=Sites,CN=Configuration,DC=ad,DC=samba,DC=example,DC=com @@ -893,6 +901,8 @@ cn: WIN05 objectGUID: 5e96a611-18b0-492a-8fb3-20fee8585990 systemFlags: 1375731712 dNSHostName: win05.ad.samba.example.com +serverReference: CN=WIN05,OU=Domain Controllers,DC=ad,DC=samba,DC=example,DC=c + om whenChanged: 20150227014848.0Z dn: CN=WIN06,CN=Servers,CN=Site-3,CN=Sites,CN=Configuration,DC=ad,DC=samba,DC=example,DC=com @@ -902,6 +912,8 @@ cn: WIN06 objectGUID: 76a6d120-851a-4473-b411-a2daee099a74 systemFlags: 1375731712 dNSHostName: WIN06.ad.samba.example.com +serverReference: CN=WIN06,OU=Domain Controllers,DC=ad,DC=samba,DC=example,DC=c + om whenChanged: 20150227014848.0Z dn: CN=WIN07,CN=Servers,CN=Site-4,CN=Sites,CN=Configuration,DC=ad,DC=samba,DC=example,DC=com @@ -911,6 +923,8 @@ cn: WIN07 objectGUID: 9cc8a7d9-d9c8-45c8-bf1b-1cc3b0b8cb00 systemFlags: 1375731712 dNSHostName: win07.ad.samba.example.com +serverReference: CN=WIN07,OU=Domain Controllers,DC=ad,DC=samba,DC=example,DC=c + om whenChanged: 20150227014849.0Z dn: CN=WIN08,CN=Servers,CN=Site-4,CN=Sites,CN=Configuration,DC=ad,DC=samba,DC=example,DC=com @@ -920,6 +934,8 @@ cn: WIN08 objectGUID: a5706cae-98a9-4b30-a9eb-6ba3601200cc systemFlags: 1375731712 dNSHostName: WIN08.ad.samba.example.com +serverReference: CN=WIN08,OU=Domain Controllers,DC=ad,DC=samba,DC=example,DC=c + om whenChanged: 20150227014848.0Z dn: CN=WIN09,CN=Servers,CN=Site-5,CN=Sites,CN=Configuration,DC=ad,DC=samba,DC=example,DC=com @@ -929,6 +945,8 @@ cn: WIN09 objectGUID: 08fbe297-70e3-40d4-aab5-f5cfce191d2e systemFlags: 1375731712 dNSHostName: win09.ad.samba.example.com +serverReference: CN=WIN09,OU=Domain Controllers,DC=ad,DC=samba,DC=example,DC=c + om whenChanged: 20150227014848.0Z dn: CN=WIN10,CN=Servers,CN=Site-5,CN=Sites,CN=Configuration,DC=ad,DC=samba,DC=example,DC=com @@ -938,6 +956,814 @@ cn: WIN10 objectGUID: bd4375f0-fa5b-49fa-9c66-8935e6bd2115 systemFlags: 1375731712 dNSHostName: win10.ad.samba.example.com +serverReference: CN=WIN10,OU=Domain Controllers,DC=ad,DC=samba,DC=example,DC=c + om +whenChanged: 20150227014849.0Z + +dn: CN=SYSVOL Subscription,CN=Domain System Volume,CN=DFSR-LocalSettings,CN=WIN01,OU=Domain Controllers,DC=ad,DC=samba,DC=example,DC=com +objectClass: top +objectClass: msDFSR-Subscription +cn: SYSVOL Subscription +whenChanged: 20150225004811.0Z +objectGUID: de7b7a4d-95bb-40e9-96e9-df49c7a73521 + +dn: CN=WIN01,OU=Domain Controllers,DC=ad,DC=samba,DC=example,DC=com +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: user +objectClass: computer +cn: WIN01 +whenChanged: 20150225004810.0Z +objectGUID: 89fb136b-eccc-42cb-a5a1-5e596738a9c7 +sAMAccountName: WIN01$ +dNSHostName: win01.ad.samba.example.com +rIDSetReferences: CN=RID Set,CN=WIN01,OU=Domain Controllers,DC=ad,DC=samba,DC= + example,DC=com +servicePrincipalName: ldap/win01.ad.samba.example.com/ad.samba.example.com +servicePrincipalName: ldap/win01.ad.samba.example.com +servicePrincipalName: ldap/WIN01 +servicePrincipalName: ldap/win01.ad.samba.example.com/AD +servicePrincipalName: ldap/ee26ae37-b7b9-46ef-9a0b-3977b89dfeb6._msdcs.ad.samb + a.example.com +servicePrincipalName: ldap/WIN01/AD +servicePrincipalName: E3514235-4B06-11D1-AB04-00C04FC2DCD2/ee26ae37-b7b9-46ef- + 9a0b-3977b89dfeb6/ad.samba.example.com +servicePrincipalName: HOST/win01.ad.samba.example.com/ad.samba.example.com +servicePrincipalName: HOST/win01.ad.samba.example.com +servicePrincipalName: HOST/WIN01 +servicePrincipalName: HOST/win01.ad.samba.example.com/AD +servicePrincipalName: HOST/WIN01/AD +servicePrincipalName: RPC/ee26ae37-b7b9-46ef-9a0b-3977b89dfeb6._msdcs.ad.samba + .example.com +servicePrincipalName: RestrictedKrbHost/WIN01 +servicePrincipalName: RestrictedKrbHost/win01.ad.samba.example.com +servicePrincipalName: GC/win01.ad.samba.example.com/ad.samba.example.com +servicePrincipalName: DNS/win01.ad.samba.example.com +servicePrincipalName: TERMSRV/win01.ad.samba.example.com +servicePrincipalName: TERMSRV/WIN01 +servicePrincipalName: ldap/win01.ad.samba.example.com/DomainDnsZones.ad.samba. + example.com +servicePrincipalName: ldap/win01.ad.samba.example.com/ForestDnsZones.ad.samba. + example.com +servicePrincipalName: Dfsr-12F9A27C-BF97-4787-9364-D31B6C55EB04/win01.ad.samba + .example.com + +dn: CN=DFSR-LocalSettings,CN=WIN01,OU=Domain Controllers,DC=ad,DC=samba,DC=example,DC=com +objectClass: top +objectClass: msDFSR-LocalSettings +cn: DFSR-LocalSettings +whenChanged: 20150225004811.0Z +objectGUID: 4d1b4151-49ca-4e0a-b54b-5372759865e1 + +dn: CN=RID Set,CN=WIN01,OU=Domain Controllers,DC=ad,DC=samba,DC=example,DC=com +objectClass: top +objectClass: rIDSet +cn: RID Set +whenChanged: 20150227001454.0Z +objectGUID: d6290f2e-ec71-440f-a3d9-5cfcc2a84975 + +dn: CN=Domain System Volume,CN=DFSR-LocalSettings,CN=WIN01,OU=Domain Controllers,DC=ad,DC=samba,DC=example,DC=com +objectClass: top +objectClass: msDFSR-Subscriber +cn: Domain System Volume +objectGUID: a0847899-913e-43d0-ac20-92011b6a62e1 +whenChanged: 20150227014849.0Z + +dn: CN=Windows Virtual Machine,CN=WIN01,OU=Domain Controllers,DC=ad,DC=samba,DC=example,DC=com +objectClass: top +objectClass: leaf +objectClass: connectionPoint +objectClass: serviceConnectionPoint +cn: Windows Virtual Machine +whenChanged: 20150226002555.0Z +objectGUID: 59ebdef8-2177-4889-80bc-ba484d910171 + +dn: CN=SYSVOL Subscription,CN=Domain System Volume,CN=DFSR-LocalSettings,CN=WIN02,OU=Domain Controllers,DC=ad,DC=samba,DC=example,DC=com +objectClass: top +objectClass: msDFSR-Subscription +cn: SYSVOL Subscription +whenChanged: 20150225013212.0Z +objectGUID: 997abd6a-50dc-4539-ad97-f873268e8e8f + +dn: CN=DFSR-LocalSettings,CN=WIN02,OU=Domain Controllers,DC=ad,DC=samba,DC=example,DC=com +objectClass: top +objectClass: msDFSR-LocalSettings +cn: DFSR-LocalSettings +whenChanged: 20150225013716.0Z +objectGUID: 73470aaa-5f15-40d7-add6-0699eb5e4774 + +dn: CN=WIN02,OU=Domain Controllers,DC=ad,DC=samba,DC=example,DC=com +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: user +objectClass: computer +cn: WIN02 +whenChanged: 20150226021838.0Z +objectGUID: 487f5009-f4ec-4110-bf40-232b3f0cc64b +sAMAccountName: WIN02$ +dNSHostName: win02.ad.samba.example.com +rIDSetReferences: CN=RID Set,CN=WIN02,OU=Domain Controllers,DC=ad,DC=samba,DC= + example,DC=com +servicePrincipalName: HOST/win02.ad.samba.example.com +servicePrincipalName: RestrictedKrbHost/win02.ad.samba.example.com +servicePrincipalName: HOST/WIN02 +servicePrincipalName: RestrictedKrbHost/WIN02 +servicePrincipalName: E3514235-4B06-11D1-AB04-00C04FC2DCD2/11a7fb87-5912-4ce6- + 92af-ef92f8f82f04/ad.samba.example.com +servicePrincipalName: TERMSRV/WIN02 +servicePrincipalName: TERMSRV/win02.ad.samba.example.com +servicePrincipalName: ldap/win02.ad.samba.example.com/AD +servicePrincipalName: ldap/WIN02 +servicePrincipalName: ldap/win02.ad.samba.example.com +servicePrincipalName: ldap/win02.ad.samba.example.com/DomainDnsZones.ad.samba. + example.com +servicePrincipalName: ldap/win02.ad.samba.example.com/ForestDnsZones.ad.samba. + example.com +servicePrincipalName: ldap/win02.ad.samba.example.com/ad.samba.example.com +servicePrincipalName: ldap/WIN02/AD +servicePrincipalName: HOST/win02.ad.samba.example.com/AD +servicePrincipalName: RPC/11a7fb87-5912-4ce6-92af-ef92f8f82f04._msdcs.ad.samba + .example.com +servicePrincipalName: GC/win02.ad.samba.example.com/ad.samba.example.com +servicePrincipalName: HOST/win02.ad.samba.example.com/ad.samba.example.com +servicePrincipalName: HOST/WIN02/AD +servicePrincipalName: ldap/11a7fb87-5912-4ce6-92af-ef92f8f82f04._msdcs.ad.samb + a.example.com +servicePrincipalName: DNS/win02.ad.samba.example.com +servicePrincipalName: Dfsr-12F9A27C-BF97-4787-9364-D31B6C55EB04/win02.ad.samba + .example.com + +dn: CN=Domain System Volume,CN=DFSR-LocalSettings,CN=WIN02,OU=Domain Controllers,DC=ad,DC=samba,DC=example,DC=com +objectClass: top +objectClass: msDFSR-Subscriber +cn: Domain System Volume +objectGUID: c9a6947e-625e-4cc7-ad24-c8ef28453385 +whenChanged: 20150227014849.0Z + +dn: CN=Windows Virtual Machine,CN=WIN02,OU=Domain Controllers,DC=ad,DC=samba,DC=example,DC=com +objectClass: top +objectClass: leaf +objectClass: connectionPoint +objectClass: serviceConnectionPoint +cn: Windows Virtual Machine +whenChanged: 20150225013151.0Z +objectGUID: 7392f4a2-ea4a-4c43-bc0f-0423943f5f58 + +dn: CN=RID Set,CN=WIN02,OU=Domain Controllers,DC=ad,DC=samba,DC=example,DC=com +objectClass: top +objectClass: rIDSet +cn: RID Set +whenChanged: 20150226224544.0Z +objectGUID: b3d516b1-c639-4791-a9a5-3f5c4309ecb7 + +dn: CN=Windows Virtual Machine,CN=WIN03,OU=Domain Controllers,DC=ad,DC=samba,DC=example,DC=com +objectClass: top +objectClass: leaf +objectClass: connectionPoint +objectClass: serviceConnectionPoint +cn: Windows Virtual Machine +whenChanged: 20150225011556.0Z +objectGUID: ef0c80ab-bd6e-4bc7-b8a4-19b5b9bc985c + +dn: CN=DFSR-LocalSettings,CN=WIN03,OU=Domain Controllers,DC=ad,DC=samba,DC=example,DC=com +objectClass: top +objectClass: msDFSR-LocalSettings +cn: DFSR-LocalSettings +whenChanged: 20150225012134.0Z +objectGUID: c03fded8-5f7a-44b5-bf33-83f4256639aa + +dn: CN=RID Set,CN=WIN03,OU=Domain Controllers,DC=ad,DC=samba,DC=example,DC=com +objectClass: top +objectClass: rIDSet +cn: RID Set +whenChanged: 20150226224730.0Z +objectGUID: 3d606baf-1c77-48ce-b2b5-2832d49b981f + +dn: CN=Domain System Volume,CN=DFSR-LocalSettings,CN=WIN03,OU=Domain Controllers,DC=ad,DC=samba,DC=example,DC=com +objectClass: top +objectClass: msDFSR-Subscriber +cn: Domain System Volume +objectGUID: b1ec7bce-7e5f-434d-a746-72b642b1fe43 +whenChanged: 20150227014849.0Z + +dn: CN=SYSVOL Subscription,CN=Domain System Volume,CN=DFSR-LocalSettings,CN=WIN03,OU=Domain Controllers,DC=ad,DC=samba,DC=example,DC=com +objectClass: top +objectClass: msDFSR-Subscription +cn: SYSVOL Subscription +whenChanged: 20150225011630.0Z +objectGUID: e6c2eee1-0ad1-4e92-808f-5ffebbe401d5 + +dn: CN=WIN03,OU=Domain Controllers,DC=ad,DC=samba,DC=example,DC=com +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: user +objectClass: computer +cn: WIN03 +whenChanged: 20150226021929.0Z +objectGUID: 180b0983-1802-443a-8380-3c94387b186a +sAMAccountName: WIN03$ +dNSHostName: win03.ad.samba.example.com +rIDSetReferences: CN=RID Set,CN=WIN03,OU=Domain Controllers,DC=ad,DC=samba,DC= + example,DC=com +servicePrincipalName: HOST/win03.ad.samba.example.com +servicePrincipalName: RestrictedKrbHost/win03.ad.samba.example.com +servicePrincipalName: HOST/WIN03 +servicePrincipalName: RestrictedKrbHost/WIN03 +servicePrincipalName: E3514235-4B06-11D1-AB04-00C04FC2DCD2/f2aa9716-c8ab-4f37- + b37d-c20be7533fa0/ad.samba.example.com +servicePrincipalName: WSMAN/win03.ad.samba.example.com +servicePrincipalName: WSMAN/win03 +servicePrincipalName: TERMSRV/win03.ad.samba.example.com +servicePrincipalName: TERMSRV/WIN03 +servicePrincipalName: ldap/win03.ad.samba.example.com +servicePrincipalName: ldap/win03.ad.samba.example.com/ForestDnsZones.ad.samba. + example.com +servicePrincipalName: ldap/win03.ad.samba.example.com/DomainDnsZones.ad.samba. + example.com +servicePrincipalName: ldap/win03.ad.samba.example.com/ad.samba.example.com +servicePrincipalName: ldap/f2aa9716-c8ab-4f37-b37d-c20be7533fa0._msdcs.ad.samb + a.example.com +servicePrincipalName: HOST/WIN03/AD +servicePrincipalName: ldap/win03.ad.samba.example.com/AD +servicePrincipalName: ldap/WIN03 +servicePrincipalName: ldap/WIN03/AD +servicePrincipalName: HOST/win03.ad.samba.example.com/AD +servicePrincipalName: RPC/f2aa9716-c8ab-4f37-b37d-c20be7533fa0._msdcs.ad.samba + .example.com +servicePrincipalName: GC/win03.ad.samba.example.com/ad.samba.example.com +servicePrincipalName: HOST/win03.ad.samba.example.com/ad.samba.example.com +servicePrincipalName: DNS/win03.ad.samba.example.com +servicePrincipalName: Dfsr-12F9A27C-BF97-4787-9364-D31B6C55EB04/win03.ad.samba + .example.com + +dn: CN=Domain System Volume,CN=DFSR-LocalSettings,CN=WIN04,OU=Domain Controllers,DC=ad,DC=samba,DC=example,DC=com +objectClass: top +objectClass: msDFSR-Subscriber +cn: Domain System Volume +objectGUID: c5439e0e-5fa0-437a-89eb-ee32268c22b0 +whenChanged: 20150227014849.0Z + +dn: CN=WIN04,OU=Domain Controllers,DC=ad,DC=samba,DC=example,DC=com +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: user +objectClass: computer +cn: WIN04 +whenChanged: 20150226022020.0Z +objectGUID: 04cf2a74-92bb-4bff-842d-5c93298f8ee2 +sAMAccountName: WIN04$ +dNSHostName: win04.ad.samba.example.com +rIDSetReferences: CN=RID Set,CN=WIN04,OU=Domain Controllers,DC=ad,DC=samba,DC= + example,DC=com +servicePrincipalName: HOST/win04.ad.samba.example.com +servicePrincipalName: RestrictedKrbHost/win04.ad.samba.example.com +servicePrincipalName: HOST/WIN04 +servicePrincipalName: RestrictedKrbHost/WIN04 +servicePrincipalName: E3514235-4B06-11D1-AB04-00C04FC2DCD2/e8e1ef96-793b-41d9- + b60c-14b48fb2da87/ad.samba.example.com +servicePrincipalName: WSMAN/win04.ad.samba.example.com +servicePrincipalName: WSMAN/win04 +servicePrincipalName: TERMSRV/win04.ad.samba.example.com +servicePrincipalName: TERMSRV/WIN04 +servicePrincipalName: Dfsr-12F9A27C-BF97-4787-9364-D31B6C55EB04/win04.ad.samba + .example.com +servicePrincipalName: HOST/win04.ad.samba.example.com/AD +servicePrincipalName: RPC/e8e1ef96-793b-41d9-b60c-14b48fb2da87._msdcs.ad.samba + .example.com +servicePrincipalName: GC/win04.ad.samba.example.com/ad.samba.example.com +servicePrincipalName: HOST/win04.ad.samba.example.com/ad.samba.example.com +servicePrincipalName: HOST/WIN04/AD +servicePrincipalName: ldap/e8e1ef96-793b-41d9-b60c-14b48fb2da87._msdcs.ad.samb + a.example.com +servicePrincipalName: ldap/WIN04/AD +servicePrincipalName: ldap/WIN04 +servicePrincipalName: ldap/win04.ad.samba.example.com/AD +servicePrincipalName: ldap/win04.ad.samba.example.com +servicePrincipalName: ldap/win04.ad.samba.example.com/ad.samba.example.com +servicePrincipalName: ldap/win04.ad.samba.example.com/DomainDnsZones.ad.samba. + example.com +servicePrincipalName: ldap/win04.ad.samba.example.com/ForestDnsZones.ad.samba. + example.com +servicePrincipalName: DNS/win04.ad.samba.example.com + +dn: CN=RID Set,CN=WIN04,OU=Domain Controllers,DC=ad,DC=samba,DC=example,DC=com +objectClass: top +objectClass: rIDSet +cn: RID Set +whenChanged: 20150226224751.0Z +objectGUID: c6eb79b6-9241-4841-8fba-0d6deb172b09 + +dn: CN=SYSVOL Subscription,CN=Domain System Volume,CN=DFSR-LocalSettings,CN=WIN04,OU=Domain Controllers,DC=ad,DC=samba,DC=example,DC=com +objectClass: top +objectClass: msDFSR-Subscription +cn: SYSVOL Subscription +whenChanged: 20150225011636.0Z +objectGUID: 10957524-cec1-48e0-b7c9-34d1d40bf91b + +dn: CN=Windows Virtual Machine,CN=WIN04,OU=Domain Controllers,DC=ad,DC=samba,DC=example,DC=com +objectClass: top +objectClass: leaf +objectClass: connectionPoint +objectClass: serviceConnectionPoint +cn: Windows Virtual Machine +whenChanged: 20150225011621.0Z +objectGUID: 8e23f017-fb18-4b15-991a-410fe840f629 + +dn: CN=DFSR-LocalSettings,CN=WIN04,OU=Domain Controllers,DC=ad,DC=samba,DC=example,DC=com +objectClass: top +objectClass: msDFSR-LocalSettings +cn: DFSR-LocalSettings +whenChanged: 20150225012141.0Z +objectGUID: 1a76539f-43ad-4c07-b960-480b508a51ce + +dn: CN=WIN05,OU=Domain Controllers,DC=ad,DC=samba,DC=example,DC=com +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: user +objectClass: computer +cn: WIN05 +whenChanged: 20150226022508.0Z +objectGUID: a3f81d7d-7fdc-4814-ab85-b17cfee875fc +sAMAccountName: WIN05$ +dNSHostName: win05.ad.samba.example.com +rIDSetReferences: CN=RID Set,CN=WIN05,OU=Domain Controllers,DC=ad,DC=samba,DC= + example,DC=com +servicePrincipalName: HOST/win05.ad.samba.example.com +servicePrincipalName: RestrictedKrbHost/win05.ad.samba.example.com +servicePrincipalName: HOST/WIN05 +servicePrincipalName: RestrictedKrbHost/WIN05 +servicePrincipalName: E3514235-4B06-11D1-AB04-00C04FC2DCD2/60430017-2cce-414b- + 8f37-08a924ae99b7/ad.samba.example.com +servicePrincipalName: TERMSRV/win05.ad.samba.example.com +servicePrincipalName: TERMSRV/WIN05 +servicePrincipalName: Dfsr-12F9A27C-BF97-4787-9364-D31B6C55EB04/win05.ad.samba + .example.com +servicePrincipalName: WSMAN/win05 +servicePrincipalName: WSMAN/win05.ad.samba.example.com +servicePrincipalName: ldap/win05.ad.samba.example.com/DomainDnsZones.ad.samba. + example.com +servicePrincipalName: ldap/win05.ad.samba.example.com/ForestDnsZones.ad.samba. + example.com +servicePrincipalName: ldap/win05.ad.samba.example.com/ad.samba.example.com +servicePrincipalName: ldap/WIN05/AD +servicePrincipalName: ldap/60430017-2cce-414b-8f37-08a924ae99b7._msdcs.ad.samb + a.example.com +servicePrincipalName: HOST/win05.ad.samba.example.com/ad.samba.example.com +servicePrincipalName: HOST/WIN05/AD +servicePrincipalName: ldap/win05.ad.samba.example.com +servicePrincipalName: ldap/WIN05 +servicePrincipalName: ldap/win05.ad.samba.example.com/AD +servicePrincipalName: HOST/win05.ad.samba.example.com/AD +servicePrincipalName: RPC/60430017-2cce-414b-8f37-08a924ae99b7._msdcs.ad.samba + .example.com +servicePrincipalName: GC/win05.ad.samba.example.com/ad.samba.example.com +servicePrincipalName: DNS/win05.ad.samba.example.com + +dn: CN=Windows Virtual Machine,CN=WIN05,OU=Domain Controllers,DC=ad,DC=samba,DC=example,DC=com +objectClass: top +objectClass: leaf +objectClass: connectionPoint +objectClass: serviceConnectionPoint +cn: Windows Virtual Machine +whenChanged: 20150225012700.0Z +objectGUID: 092022cd-afc3-457a-b596-c16c03d573d1 + +dn: CN=SYSVOL Subscription,CN=Domain System Volume,CN=DFSR-LocalSettings,CN=WIN05,OU=Domain Controllers,DC=ad,DC=samba,DC=example,DC=com +objectClass: top +objectClass: msDFSR-Subscription +cn: SYSVOL Subscription +whenChanged: 20150225012724.0Z +objectGUID: f9099cf3-1af3-4b5f-9cba-b5c78189bc4a + +dn: CN=RID Set,CN=WIN05,OU=Domain Controllers,DC=ad,DC=samba,DC=example,DC=com +objectClass: top +objectClass: rIDSet +cn: RID Set +whenChanged: 20150226224836.0Z +objectGUID: 9d3e43b2-84cc-45a4-8948-8791592a25ba + +dn: CN=DFSR-LocalSettings,CN=WIN05,OU=Domain Controllers,DC=ad,DC=samba,DC=example,DC=com +objectClass: top +objectClass: msDFSR-LocalSettings +cn: DFSR-LocalSettings +whenChanged: 20150225013228.0Z +objectGUID: 19b58264-9ff3-4cd9-9f9f-70797d3aa6d6 + +dn: CN=Domain System Volume,CN=DFSR-LocalSettings,CN=WIN05,OU=Domain Controllers,DC=ad,DC=samba,DC=example,DC=com +objectClass: top +objectClass: msDFSR-Subscriber +cn: Domain System Volume +objectGUID: 7a5ef091-2906-47b3-a190-ca98122b704d +whenChanged: 20150227014849.0Z + +dn: CN=Windows Virtual Machine,CN=WIN06,OU=Domain Controllers,DC=ad,DC=samba,DC=example,DC=com +objectClass: top +objectClass: leaf +objectClass: connectionPoint +objectClass: serviceConnectionPoint +cn: Windows Virtual Machine +whenChanged: 20150225013648.0Z +objectGUID: 7ec56e27-1658-4cac-af21-fc4c06e41951 + +dn: CN=WIN06,OU=Domain Controllers,DC=ad,DC=samba,DC=example,DC=com +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: user +objectClass: computer +cn: WIN06 +objectGUID: 6e12d598-4d94-4f40-a04b-106b12c94114 +sAMAccountName: WIN06$ +dNSHostName: WIN06.ad.samba.example.com +servicePrincipalName: HOST/WIN06 +servicePrincipalName: HOST/WIN06.ad.samba.example.com +servicePrincipalName: RestrictedKrbHost/win06.ad.samba.example.com +servicePrincipalName: RestrictedKrbHost/WIN06 +servicePrincipalName: Dfsr-12F9A27C-BF97-4787-9364-D31B6C55EB04/win06.ad.samba + .example.com +servicePrincipalName: TERMSRV/WIN06 +servicePrincipalName: TERMSRV/win06.ad.samba.example.com +servicePrincipalName: HOST/WIN06/AD +servicePrincipalName: ldap/win06.ad.samba.example.com/ad.samba.example.com +servicePrincipalName: ldap/win06.ad.samba.example.com/ForestDnsZones.ad.samba. + example.com +servicePrincipalName: ldap/win06.ad.samba.example.com/DomainDnsZones.ad.samba. + example.com +servicePrincipalName: ldap/win06.ad.samba.example.com +servicePrincipalName: ldap/WIN06 +servicePrincipalName: ldap/win06.ad.samba.example.com/AD +servicePrincipalName: ldap/2b0d1d67-6829-4951-bc71-42b4d14607c4._msdcs.ad.samb + a.example.com +servicePrincipalName: ldap/WIN06/AD +servicePrincipalName: HOST/win06.ad.samba.example.com/ad.samba.example.com +servicePrincipalName: RPC/2b0d1d67-6829-4951-bc71-42b4d14607c4._msdcs.ad.samba + .example.com +servicePrincipalName: GC/win06.ad.samba.example.com/ad.samba.example.com +servicePrincipalName: HOST/win06.ad.samba.example.com/AD +msDS-KrbTgtLink: CN=krbtgt_35360,CN=Users,DC=ad,DC=samba,DC=example,DC=com +whenChanged: 20150227014849.0Z + +dn: CN=DFSR-LocalSettings,CN=WIN06,OU=Domain Controllers,DC=ad,DC=samba,DC=example,DC=com +objectClass: top +objectClass: msDFSR-LocalSettings +cn: DFSR-LocalSettings +whenChanged: 20150225013711.0Z +objectGUID: 1aa3bdd7-0854-4eda-8ecd-924739a1198e + +dn: CN=Domain System Volume,CN=DFSR-LocalSettings,CN=WIN06,OU=Domain Controllers,DC=ad,DC=samba,DC=example,DC=com +objectClass: top +objectClass: msDFSR-Subscriber +cn: Domain System Volume +objectGUID: 98328098-76e8-4427-baa0-b28eafd4a6ec +whenChanged: 20150227014849.0Z + +dn: CN=SYSVOL Subscription,CN=Domain System Volume,CN=DFSR-LocalSettings,CN=WIN06,OU=Domain Controllers,DC=ad,DC=samba,DC=example,DC=com +objectClass: top +objectClass: msDFSR-Subscription +cn: SYSVOL Subscription +whenChanged: 20150225013709.0Z +objectGUID: 546da7b6-0bbf-4aed-a3ae-732f63eea5d8 + +dn: CN=krbtgt_35360,CN=Users,DC=ad,DC=samba,DC=example,DC=com +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: user +cn: krbtgt_35360 +whenChanged: 20150225013053.0Z +objectGUID: c60403a2-46f6-475e-88ff-424b6fa46e24 +sAMAccountName: krbtgt_35360 + +dn: CN=RID Set,CN=WIN07,OU=Domain Controllers,DC=ad,DC=samba,DC=example,DC=com +objectClass: top +objectClass: rIDSet +cn: RID Set +whenChanged: 20150226224857.0Z +objectGUID: 25bf0a90-ce7d-4f3b-a5aa-24ec30253a8e + +dn: CN=WIN07,OU=Domain Controllers,DC=ad,DC=samba,DC=example,DC=com +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: user +objectClass: computer +cn: WIN07 +objectGUID: 2f43ad87-8daf-4137-a68e-db21d9ee7e74 +sAMAccountName: WIN07$ +dNSHostName: win07.ad.samba.example.com +rIDSetReferences: CN=RID Set,CN=WIN07,OU=Domain Controllers,DC=ad,DC=samba,DC= + example,DC=com +servicePrincipalName: HOST/win07.ad.samba.example.com +servicePrincipalName: RestrictedKrbHost/win07.ad.samba.example.com +servicePrincipalName: HOST/WIN07 +servicePrincipalName: RestrictedKrbHost/WIN07 +servicePrincipalName: TERMSRV/WIN07 +servicePrincipalName: TERMSRV/win07.ad.samba.example.com +servicePrincipalName: Dfsr-12F9A27C-BF97-4787-9364-D31B6C55EB04/win07.ad.samba + .example.com +servicePrincipalName: E3514235-4B06-11D1-AB04-00C04FC2DCD2/3f700c7f-03c4-4ee0- + a8b6-dd561cc1b6be/ad.samba.example.com +servicePrincipalName: HOST/win07.ad.samba.example.com/AD +servicePrincipalName: RPC/3f700c7f-03c4-4ee0-a8b6-dd561cc1b6be._msdcs.ad.samba + .example.com +servicePrincipalName: GC/win07.ad.samba.example.com/ad.samba.example.com +servicePrincipalName: HOST/win07.ad.samba.example.com/ad.samba.example.com +servicePrincipalName: ldap/win07.ad.samba.example.com/AD +servicePrincipalName: ldap/3f700c7f-03c4-4ee0-a8b6-dd561cc1b6be._msdcs.ad.samb + a.example.com +servicePrincipalName: HOST/WIN07/AD +servicePrincipalName: ldap/WIN07/AD +servicePrincipalName: ldap/win07.ad.samba.example.com/ad.samba.example.com +servicePrincipalName: ldap/win07.ad.samba.example.com/ForestDnsZones.ad.samba. + example.com +servicePrincipalName: ldap/win07.ad.samba.example.com/DomainDnsZones.ad.samba. + example.com +servicePrincipalName: ldap/win07.ad.samba.example.com +servicePrincipalName: ldap/WIN07 +servicePrincipalName: DNS/win07.ad.samba.example.com +whenChanged: 20150227014849.0Z + +dn: CN=DFSR-LocalSettings,CN=WIN07,OU=Domain Controllers,DC=ad,DC=samba,DC=example,DC=com +objectClass: top +objectClass: msDFSR-LocalSettings +cn: DFSR-LocalSettings +whenChanged: 20150225013610.0Z +objectGUID: 0e2fef27-2fec-48e7-869b-bdfa458888ec + +dn: CN=Domain System Volume,CN=DFSR-LocalSettings,CN=WIN07,OU=Domain Controllers,DC=ad,DC=samba,DC=example,DC=com +objectClass: top +objectClass: msDFSR-Subscriber +cn: Domain System Volume +objectGUID: 86ef821c-afb2-4b60-839a-6a6ceda9559a +whenChanged: 20150227014849.0Z + +dn: CN=Windows Virtual Machine,CN=WIN07,OU=Domain Controllers,DC=ad,DC=samba,DC=example,DC=com +objectClass: top +objectClass: leaf +objectClass: connectionPoint +objectClass: serviceConnectionPoint +cn: Windows Virtual Machine +whenChanged: 20150225013033.0Z +objectGUID: 3c9b41cf-d1da-416a-8f19-bed987f08336 + +dn: CN=SYSVOL Subscription,CN=Domain System Volume,CN=DFSR-LocalSettings,CN=WIN07,OU=Domain Controllers,DC=ad,DC=samba,DC=example,DC=com +objectClass: top +objectClass: msDFSR-Subscription +cn: SYSVOL Subscription +whenChanged: 20150225013052.0Z +objectGUID: c82d8eff-6e1f-4df0-93af-90be489fd41f + +dn: CN=SYSVOL Subscription,CN=Domain System Volume,CN=DFSR-LocalSettings,CN=WIN08,OU=Domain Controllers,DC=ad,DC=samba,DC=example,DC=com +objectClass: top +objectClass: msDFSR-Subscription +cn: SYSVOL Subscription +whenChanged: 20150225014214.0Z +objectGUID: 27c4c328-4d6f-434d-8043-f98c7d9098c2 + +dn: CN=DFSR-LocalSettings,CN=WIN08,OU=Domain Controllers,DC=ad,DC=samba,DC=example,DC=com +objectClass: top +objectClass: msDFSR-LocalSettings +cn: DFSR-LocalSettings +whenChanged: 20150225014215.0Z +objectGUID: 5474dd4b-9c18-49c1-8443-4cac1d1fc312 + +dn: CN=Windows Virtual Machine,CN=WIN08,OU=Domain Controllers,DC=ad,DC=samba,DC=example,DC=com +objectClass: top +objectClass: leaf +objectClass: connectionPoint +objectClass: serviceConnectionPoint +cn: Windows Virtual Machine +whenChanged: 20150225014212.0Z +objectGUID: ee738795-9b7a-4cab-9444-9719481bc633 + +dn: CN=Domain System Volume,CN=DFSR-LocalSettings,CN=WIN08,OU=Domain Controllers,DC=ad,DC=samba,DC=example,DC=com +objectClass: top +objectClass: msDFSR-Subscriber +cn: Domain System Volume +objectGUID: 521469b8-3258-4e5a-b64e-b584d13c72d5 +whenChanged: 20150227014849.0Z + +dn: CN=WIN08,OU=Domain Controllers,DC=ad,DC=samba,DC=example,DC=com +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: user +objectClass: computer +cn: WIN08 +objectGUID: 02501176-18a0-4bff-b935-9e56ac9e1897 +sAMAccountName: WIN08$ +dNSHostName: WIN08.ad.samba.example.com +servicePrincipalName: HOST/WIN08 +servicePrincipalName: HOST/WIN08.ad.samba.example.com +servicePrincipalName: RestrictedKrbHost/win08.ad.samba.example.com +servicePrincipalName: RestrictedKrbHost/WIN08 +servicePrincipalName: TERMSRV/win08.ad.samba.example.com +servicePrincipalName: TERMSRV/WIN08 +servicePrincipalName: Dfsr-12F9A27C-BF97-4787-9364-D31B6C55EB04/win08.ad.samba + .example.com +servicePrincipalName: ldap/win08.ad.samba.example.com/ad.samba.example.com +servicePrincipalName: ldap/win08.ad.samba.example.com/ForestDnsZones.ad.samba. + example.com +servicePrincipalName: ldap/win08.ad.samba.example.com/DomainDnsZones.ad.samba. + example.com +servicePrincipalName: ldap/win08.ad.samba.example.com +servicePrincipalName: ldap/WIN08 +servicePrincipalName: ldap/win08.ad.samba.example.com/AD +servicePrincipalName: ldap/30b4cb8e-324a-41fc-9f73-47ad8dd07ded._msdcs.ad.samb + a.example.com +servicePrincipalName: ldap/WIN08/AD +servicePrincipalName: HOST/win08.ad.samba.example.com/ad.samba.example.com +servicePrincipalName: RPC/30b4cb8e-324a-41fc-9f73-47ad8dd07ded._msdcs.ad.samba + .example.com +servicePrincipalName: GC/win08.ad.samba.example.com/ad.samba.example.com +servicePrincipalName: HOST/win08.ad.samba.example.com/AD +servicePrincipalName: HOST/WIN08/AD +servicePrincipalName: WSMAN/win08.ad.samba.example.com +servicePrincipalName: WSMAN/win08 +msDS-KrbTgtLink: CN=krbtgt_59152,CN=Users,DC=ad,DC=samba,DC=example,DC=com +whenChanged: 20150227014849.0Z + +dn: CN=krbtgt_59152,CN=Users,DC=ad,DC=samba,DC=example,DC=com +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: user +cn: krbtgt_59152 +whenChanged: 20150225013729.0Z +objectGUID: 67029794-8288-48b6-bb2c-04390b46b981 +sAMAccountName: krbtgt_59152 + +dn: CN=DFSR-LocalSettings,CN=WIN09,OU=Domain Controllers,DC=ad,DC=samba,DC=example,DC=com +objectClass: top +objectClass: msDFSR-LocalSettings +cn: DFSR-LocalSettings +whenChanged: 20150225014619.0Z +objectGUID: b65f0321-e47a-4d77-b73e-e2fa154f6006 + +dn: CN=Domain System Volume,CN=DFSR-LocalSettings,CN=WIN09,OU=Domain Controllers,DC=ad,DC=samba,DC=example,DC=com +objectClass: top +objectClass: msDFSR-Subscriber +cn: Domain System Volume +objectGUID: 5a20dba5-44ac-4e4f-ab71-e6122736d4b4 +whenChanged: 20150227014849.0Z + +dn: CN=SYSVOL Subscription,CN=Domain System Volume,CN=DFSR-LocalSettings,CN=WIN09,OU=Domain Controllers,DC=ad,DC=samba,DC=example,DC=com +objectClass: top +objectClass: msDFSR-Subscription +cn: SYSVOL Subscription +whenChanged: 20150225014100.0Z +objectGUID: 723bd88a-e183-41cb-882b-653b59183b99 + +dn: CN=WIN09,OU=Domain Controllers,DC=ad,DC=samba,DC=example,DC=com +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: user +objectClass: computer +cn: WIN09 +whenChanged: 20150226022536.0Z +objectGUID: 4061b71b-3cf5-4732-a2d3-d5fd2cd9d9ff +sAMAccountName: WIN09$ +dNSHostName: win09.ad.samba.example.com +rIDSetReferences: CN=RID Set,CN=WIN09,OU=Domain Controllers,DC=ad,DC=samba,DC= + example,DC=com +servicePrincipalName: HOST/win09.ad.samba.example.com +servicePrincipalName: RestrictedKrbHost/win09.ad.samba.example.com +servicePrincipalName: HOST/WIN09 +servicePrincipalName: RestrictedKrbHost/WIN09 +servicePrincipalName: TERMSRV/WIN09 +servicePrincipalName: TERMSRV/win09.ad.samba.example.com +servicePrincipalName: Dfsr-12F9A27C-BF97-4787-9364-D31B6C55EB04/win09.ad.samba + .example.com +servicePrincipalName: E3514235-4B06-11D1-AB04-00C04FC2DCD2/93b204cb-5f34-4c66- + aac0-a58094d9d0dc/ad.samba.example.com +servicePrincipalName: ldap/win09.ad.samba.example.com/AD +servicePrincipalName: ldap/WIN09 +servicePrincipalName: ldap/win09.ad.samba.example.com +servicePrincipalName: ldap/win09.ad.samba.example.com/DomainDnsZones.ad.samba. + example.com +servicePrincipalName: ldap/93b204cb-5f34-4c66-aac0-a58094d9d0dc._msdcs.ad.samb + a.example.com +servicePrincipalName: ldap/WIN09/AD +servicePrincipalName: ldap/win09.ad.samba.example.com/ad.samba.example.com +servicePrincipalName: ldap/win09.ad.samba.example.com/ForestDnsZones.ad.samba. + example.com +servicePrincipalName: DNS/win09.ad.samba.example.com +servicePrincipalName: HOST/win09.ad.samba.example.com/AD +servicePrincipalName: RPC/93b204cb-5f34-4c66-aac0-a58094d9d0dc._msdcs.ad.samba + .example.com +servicePrincipalName: GC/win09.ad.samba.example.com/ad.samba.example.com +servicePrincipalName: HOST/win09.ad.samba.example.com/ad.samba.example.com +servicePrincipalName: HOST/WIN09/AD + +dn: CN=Windows Virtual Machine\0ACNF:23077751-5158-45b3-a973-1ac08af7d5b7,CN=WIN09,OU=Domain Controllers,DC=ad,DC=samba,DC=example,DC=com +objectClass: top +objectClass: leaf +objectClass: connectionPoint +objectClass: serviceConnectionPoint +cn:: V2luZG93cyBWaXJ0dWFsIE1hY2hpbmUKQ05GOjIzMDc3NzUxLTUxNTgtNDViMy1hOTczLTFhY + zA4YWY3ZDViNw== +whenChanged: 20150227000342.0Z +objectGUID: 23077751-5158-45b3-a973-1ac08af7d5b7 + +dn: CN=RID Set,CN=WIN09,OU=Domain Controllers,DC=ad,DC=samba,DC=example,DC=com +objectClass: top +objectClass: rIDSet +cn: RID Set +whenChanged: 20150226224919.0Z +objectGUID: 54e5a96f-3f79-49b8-a15f-f7f79e9ef073 + +dn: CN=Windows Virtual Machine,CN=WIN09,OU=Domain Controllers,DC=ad,DC=samba,DC=example,DC=com +objectClass: top +objectClass: leaf +objectClass: connectionPoint +objectClass: serviceConnectionPoint +cn: Windows Virtual Machine +whenChanged: 20150226022537.0Z +objectGUID: 3e449d58-1205-402c-ad10-50a221b5b6e3 + +dn: CN=Windows Virtual Machine,CN=WIN10,OU=Domain Controllers,DC=ad,DC=samba,DC=example,DC=com +objectClass: top +objectClass: leaf +objectClass: connectionPoint +objectClass: serviceConnectionPoint +cn: Windows Virtual Machine +whenChanged: 20150225014221.0Z +objectGUID: 3e615c58-abbd-461b-a59b-2812c209893c + +dn: CN=SYSVOL Subscription,CN=Domain System Volume,CN=DFSR-LocalSettings,CN=WIN10,OU=Domain Controllers,DC=ad,DC=samba,DC=example,DC=com +objectClass: top +objectClass: msDFSR-Subscription +cn: SYSVOL Subscription +whenChanged: 20150225014220.0Z +objectGUID: b52d27df-9ea7-4324-9b75-4daaa3200bd4 + +dn: CN=RID Set,CN=WIN10,OU=Domain Controllers,DC=ad,DC=samba,DC=example,DC=com +objectClass: top +objectClass: rIDSet +cn: RID Set +whenChanged: 20150226224941.0Z +objectGUID: 03960931-a83e-4eed-beb3-b8f8c05d353a + +dn: CN=WIN10,OU=Domain Controllers,DC=ad,DC=samba,DC=example,DC=com +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: user +objectClass: computer +cn: WIN10 +whenChanged: 20150226022732.0Z +objectGUID: 07b23f85-7524-4f35-9168-a9a965fcfff9 +sAMAccountName: WIN10$ +dNSHostName: win10.ad.samba.example.com +rIDSetReferences: CN=RID Set,CN=WIN10,OU=Domain Controllers,DC=ad,DC=samba,DC= + example,DC=com +servicePrincipalName: HOST/win10.ad.samba.example.com +servicePrincipalName: RestrictedKrbHost/win10.ad.samba.example.com +servicePrincipalName: HOST/WIN10 +servicePrincipalName: RestrictedKrbHost/WIN10 +servicePrincipalName: E3514235-4B06-11D1-AB04-00C04FC2DCD2/9559f18d-ba0f-4609- + 8cf1-ce055c83eeba/ad.samba.example.com +servicePrincipalName: TERMSRV/win10.ad.samba.example.com +servicePrincipalName: TERMSRV/WIN10 +servicePrincipalName: Dfsr-12F9A27C-BF97-4787-9364-D31B6C55EB04/win10.ad.samba + .example.com +servicePrincipalName: HOST/win10.ad.samba.example.com/AD +servicePrincipalName: RPC/9559f18d-ba0f-4609-8cf1-ce055c83eeba._msdcs.ad.samba + .example.com +servicePrincipalName: GC/win10.ad.samba.example.com/ad.samba.example.com +servicePrincipalName: HOST/win10.ad.samba.example.com/ad.samba.example.com +servicePrincipalName: ldap/WIN10/AD +servicePrincipalName: HOST/WIN10/AD +servicePrincipalName: ldap/win10.ad.samba.example.com/ad.samba.example.com +servicePrincipalName: ldap/win10.ad.samba.example.com/DomainDnsZones.ad.samba. + example.com +servicePrincipalName: ldap/win10.ad.samba.example.com/ForestDnsZones.ad.samba. + example.com +servicePrincipalName: ldap/win10.ad.samba.example.com +servicePrincipalName: ldap/WIN10 +servicePrincipalName: ldap/win10.ad.samba.example.com/AD +servicePrincipalName: ldap/9559f18d-ba0f-4609-8cf1-ce055c83eeba._msdcs.ad.samb + a.example.com +servicePrincipalName: DNS/win10.ad.samba.example.com + +dn: CN=DFSR-LocalSettings,CN=WIN10,OU=Domain Controllers,DC=ad,DC=samba,DC=example,DC=com +objectClass: top +objectClass: msDFSR-LocalSettings +cn: DFSR-LocalSettings +whenChanged: 20150225014724.0Z +objectGUID: db182d07-b912-41f2-98a6-94dd727d24ec + +dn: CN=Domain System Volume,CN=DFSR-LocalSettings,CN=WIN10,OU=Domain Controllers,DC=ad,DC=samba,DC=example,DC=com +objectClass: top +objectClass: msDFSR-Subscriber +cn: Domain System Volume +objectGUID: ada143ee-b749-44ea-9d56-6b8b325abe1b whenChanged: 20150227014849.0Z dn: DC=ad,DC=samba,DC=example,DC=com From b86f7643df0fdf165f9a1d666dc7f114058cb9d8 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Tue, 22 Sep 2015 12:10:00 +1200 Subject: [PATCH 15/36] dns_server: Put more code in common This will allow a python module to be written to modify DNS entries in sam.ldb directly Signed-off-by: Andrew Bartlett --- source4/dns_server/dns_server.c | 66 +------------ source4/dns_server/dns_server.h | 9 +- source4/dns_server/dns_utils.c | 88 +----------------- source4/dns_server/dnsserver_common.c | 170 ++++++++++++++++++++++++++++++++++ source4/dns_server/dnsserver_common.h | 18 +++- 5 files changed, 195 insertions(+), 156 deletions(-) diff --git a/source4/dns_server/dns_server.c b/source4/dns_server/dns_server.c index 66ab738..45d28a7 100644 --- a/source4/dns_server/dns_server.c +++ b/source4/dns_server/dns_server.c @@ -725,27 +725,6 @@ static NTSTATUS dns_startup_interfaces(struct dns_server *dns, return NT_STATUS_OK; } -static int dns_server_sort_zones(struct ldb_message **m1, struct ldb_message **m2) -{ - const char *n1, *n2; - size_t l1, l2; - - n1 = ldb_msg_find_attr_as_string(*m1, "name", NULL); - n2 = ldb_msg_find_attr_as_string(*m2, "name", NULL); - - l1 = strlen(n1); - l2 = strlen(n2); - - /* If the string lengths are not equal just sort by length */ - if (l1 != l2) { - /* If m1 is the larger zone name, return it first */ - return l2 - l1; - } - - /*TODO: We need to compare DNs here, we want the DomainDNSZones first */ - return 0; -} - static struct dns_server_tkey_store *tkey_store_init(TALLOC_CTX *mem_ctx, uint16_t size) { @@ -769,51 +748,14 @@ static struct dns_server_tkey_store *tkey_store_init(TALLOC_CTX *mem_ctx, static NTSTATUS dns_server_reload_zones(struct dns_server *dns) { - int ret; - static const char * const attrs[] = { "name", NULL}; - struct ldb_result *res; - int i; + NTSTATUS status; struct dns_server_zone *new_list = NULL; struct dns_server_zone *old_list = NULL; struct dns_server_zone *old_zone; - - // TODO: this search does not work against windows - ret = dsdb_search(dns->samdb, dns, &res, NULL, LDB_SCOPE_SUBTREE, - attrs, DSDB_SEARCH_SEARCH_ALL_PARTITIONS, "(objectClass=dnsZone)"); - if (ret != LDB_SUCCESS) { - return NT_STATUS_INTERNAL_DB_CORRUPTION; - } - - TYPESAFE_QSORT(res->msgs, res->count, dns_server_sort_zones); - - for (i=0; i < res->count; i++) { - struct dns_server_zone *z; - - z = talloc_zero(dns, struct dns_server_zone); - if (z == NULL) { - return NT_STATUS_NO_MEMORY; - } - - z->name = ldb_msg_find_attr_as_string(res->msgs[i], "name", NULL); - z->dn = talloc_move(z, &res->msgs[i]->dn); - /* - * Ignore the RootDNSServers zone and zones that we don't support yet - * RootDNSServers should never be returned (Windows DNS server don't) - * ..TrustAnchors should never be returned as is, (Windows returns - * TrustAnchors) and for the moment we don't support DNSSEC so we'd better - * not return this zone. - */ - if ((strcmp(z->name, "RootDNSServers") == 0) || - (strcmp(z->name, "..TrustAnchors") == 0)) - { - DEBUG(10, ("Ignoring zone %s\n", z->name)); - talloc_free(z); - continue; - } - DLIST_ADD_END(new_list, z, NULL); + status = dns_common_zones(dns->samdb, dns, &new_list); + if (!NT_STATUS_IS_OK(status)) { + return status; } - - old_list = dns->zones; dns->zones = new_list; while ((old_zone = DLIST_TAIL(old_list)) != NULL) { DLIST_REMOVE(old_list, old_zone); diff --git a/source4/dns_server/dns_server.h b/source4/dns_server/dns_server.h index 64b716a..e5c15ec 100644 --- a/source4/dns_server/dns_server.h +++ b/source4/dns_server/dns_server.h @@ -24,15 +24,9 @@ #include "librpc/gen_ndr/dns.h" #include "librpc/gen_ndr/ndr_dnsp.h" +#include "dnsserver_common.h" struct tsocket_address; - -struct dns_server_zone { - struct dns_server_zone *prev, *next; - const char *name; - struct ldb_dn *dn; -}; - struct dns_server_tkey { const char *name; enum dns_tkey_mode mode; @@ -87,7 +81,6 @@ WERROR dns_server_process_update(struct dns_server *dns, struct dns_res_rec **updates, uint16_t *update_count, struct dns_res_rec **additional, uint16_t *arcount); -bool dns_name_match(const char *zone, const char *name, size_t *host_part_len); bool dns_name_equal(const char *name1, const char *name2); bool dns_records_match(struct dnsp_DnssrvRpcRecord *rec1, struct dnsp_DnssrvRpcRecord *rec2); diff --git a/source4/dns_server/dns_utils.c b/source4/dns_server/dns_utils.c index 28412eb..3092633 100644 --- a/source4/dns_server/dns_utils.c +++ b/source4/dns_server/dns_utils.c @@ -33,47 +33,6 @@ #undef DBGC_CLASS #define DBGC_CLASS DBGC_DNS -bool dns_name_match(const char *zone, const char *name, size_t *host_part_len) -{ - size_t zl = strlen(zone); - size_t nl = strlen(name); - ssize_t zi, ni; - static const size_t fixup = 'a' - 'A'; - - if (zl > nl) { - return false; - } - - for (zi = zl, ni = nl; zi >= 0; zi--, ni--) { - char zc = zone[zi]; - char nc = name[ni]; - - /* convert to lower case */ - if (zc >= 'A' && zc <= 'Z') { - zc += fixup; - } - if (nc >= 'A' && nc <= 'Z') { - nc += fixup; - } - - if (zc != nc) { - return false; - } - } - - if (ni >= 0) { - if (name[ni] != '.') { - return false; - } - - ni--; - } - - *host_part_len = ni+1; - - return true; -} - /* Names are equal if they match and there's nothing left over */ bool dns_name_equal(const char *name1, const char *name2) { @@ -218,51 +177,10 @@ const char *dns_get_authoritative_zone(struct dns_server *dns, WERROR dns_name2dn(struct dns_server *dns, TALLOC_CTX *mem_ctx, const char *name, - struct ldb_dn **_dn) + struct ldb_dn **dn) { - struct ldb_dn *base; - struct ldb_dn *dn; - const struct dns_server_zone *z; - size_t host_part_len = 0; - - if (name == NULL) { - return DNS_ERR(FORMAT_ERROR); - } - - /*TODO: Check if 'name' is a valid DNS name */ - - if (strcmp(name, "") == 0) { - base = ldb_get_default_basedn(dns->samdb); - dn = ldb_dn_copy(mem_ctx, base); - ldb_dn_add_child_fmt(dn, "DC=@,DC=RootDNSServers,CN=MicrosoftDNS,CN=System"); - *_dn = dn; - return WERR_OK; - } - - for (z = dns->zones; z != NULL; z = z->next) { - bool match; - - match = dns_name_match(z->name, name, &host_part_len); - if (match) { - break; - } - } - - if (z == NULL) { - return DNS_ERR(NAME_ERROR); - } - - if (host_part_len == 0) { - dn = ldb_dn_copy(mem_ctx, z->dn); - ldb_dn_add_child_fmt(dn, "DC=@"); - *_dn = dn; - return WERR_OK; - } - - dn = ldb_dn_copy(mem_ctx, z->dn); - ldb_dn_add_child_fmt(dn, "DC=%*.*s", (int)host_part_len, (int)host_part_len, name); - *_dn = dn; - return WERR_OK; + return dns_common_name2dn(dns->samdb, dns->zones, + mem_ctx, name, dn); } WERROR dns_generate_options(struct dns_server *dns, diff --git a/source4/dns_server/dnsserver_common.c b/source4/dns_server/dnsserver_common.c index c49d6ec..7199ef7 100644 --- a/source4/dns_server/dnsserver_common.c +++ b/source4/dns_server/dnsserver_common.c @@ -5,6 +5,7 @@ Copyright (C) 2010 Kai Blin Copyright (C) 2014 Stefan Metzmacher + Copyright (C) 2015 Andrew Bartlett 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 @@ -30,6 +31,7 @@ #include "dsdb/samdb/samdb.h" #include "dsdb/common/util.h" #include "dns_server/dnsserver_common.h" +#include "lib/util/dlinklist.h" #undef DBGC_CLASS #define DBGC_CLASS DBGC_DNS @@ -340,3 +342,171 @@ WERROR dns_common_replace(struct ldb_context *samdb, return WERR_OK; } + +bool dns_name_match(const char *zone, const char *name, size_t *host_part_len) +{ + size_t zl = strlen(zone); + size_t nl = strlen(name); + ssize_t zi, ni; + static const size_t fixup = 'a' - 'A'; + + if (zl > nl) { + return false; + } + + for (zi = zl, ni = nl; zi >= 0; zi--, ni--) { + char zc = zone[zi]; + char nc = name[ni]; + + /* convert to lower case */ + if (zc >= 'A' && zc <= 'Z') { + zc += fixup; + } + if (nc >= 'A' && nc <= 'Z') { + nc += fixup; + } + + if (zc != nc) { + return false; + } + } + + if (ni >= 0) { + if (name[ni] != '.') { + return false; + } + + ni--; + } + + *host_part_len = ni+1; + + return true; +} + +WERROR dns_common_name2dn(struct ldb_context *samdb, + struct dns_server_zone *zones, + TALLOC_CTX *mem_ctx, + const char *name, + struct ldb_dn **_dn) +{ + struct ldb_dn *base; + struct ldb_dn *dn; + const struct dns_server_zone *z; + size_t host_part_len = 0; + + if (name == NULL) { + return DNS_ERR(FORMAT_ERROR); + } + + /*TODO: Check if 'name' is a valid DNS name */ + + if (strcmp(name, "") == 0) { + base = ldb_get_default_basedn(samdb); + dn = ldb_dn_copy(mem_ctx, base); + ldb_dn_add_child_fmt(dn, "DC=@,DC=RootDNSServers,CN=MicrosoftDNS,CN=System"); + *_dn = dn; + return WERR_OK; + } + + for (z = zones; z != NULL; z = z->next) { + bool match; + + match = dns_name_match(z->name, name, &host_part_len); + if (match) { + break; + } + } + + if (z == NULL) { + return DNS_ERR(NAME_ERROR); + } + + if (host_part_len == 0) { + dn = ldb_dn_copy(mem_ctx, z->dn); + ldb_dn_add_child_fmt(dn, "DC=@"); + *_dn = dn; + return WERR_OK; + } + + dn = ldb_dn_copy(mem_ctx, z->dn); + ldb_dn_add_child_fmt(dn, "DC=%*.*s", (int)host_part_len, (int)host_part_len, name); + *_dn = dn; + return WERR_OK; +} + +static int dns_common_sort_zones(struct ldb_message **m1, struct ldb_message **m2) +{ + const char *n1, *n2; + size_t l1, l2; + + n1 = ldb_msg_find_attr_as_string(*m1, "name", NULL); + n2 = ldb_msg_find_attr_as_string(*m2, "name", NULL); + + l1 = strlen(n1); + l2 = strlen(n2); + + /* If the string lengths are not equal just sort by length */ + if (l1 != l2) { + /* If m1 is the larger zone name, return it first */ + return l2 - l1; + } + + /*TODO: We need to compare DNs here, we want the DomainDNSZones first */ + return 0; +} + +NTSTATUS dns_common_zones(struct ldb_context *samdb, + TALLOC_CTX *mem_ctx, + struct dns_server_zone **zones_ret) +{ + int ret; + static const char * const attrs[] = { "name", NULL}; + struct ldb_result *res; + int i; + struct dns_server_zone *new_list = NULL; + TALLOC_CTX *frame = talloc_stackframe(); + + // TODO: this search does not work against windows + ret = dsdb_search(samdb, frame, &res, NULL, LDB_SCOPE_SUBTREE, + attrs, DSDB_SEARCH_SEARCH_ALL_PARTITIONS, "(objectClass=dnsZone)"); + if (ret != LDB_SUCCESS) { + TALLOC_FREE(frame); + return NT_STATUS_INTERNAL_DB_CORRUPTION; + } + + TYPESAFE_QSORT(res->msgs, res->count, dns_common_sort_zones); + + for (i=0; i < res->count; i++) { + struct dns_server_zone *z; + + z = talloc_zero(mem_ctx, struct dns_server_zone); + if (z == NULL) { + TALLOC_FREE(frame); + return NT_STATUS_NO_MEMORY; + } + + z->name = ldb_msg_find_attr_as_string(res->msgs[i], "name", NULL); + talloc_steal(z, z->name); + z->dn = talloc_move(z, &res->msgs[i]->dn); + /* + * Ignore the RootDNSServers zone and zones that we don't support yet + * RootDNSServers should never be returned (Windows DNS server don't) + * ..TrustAnchors should never be returned as is, (Windows returns + * TrustAnchors) and for the moment we don't support DNSSEC so we'd better + * not return this zone. + */ + if ((strcmp(z->name, "RootDNSServers") == 0) || + (strcmp(z->name, "..TrustAnchors") == 0)) + { + DEBUG(10, ("Ignoring zone %s\n", z->name)); + talloc_free(z); + continue; + } + DLIST_ADD_END(new_list, z, NULL); + } + + *zones_ret = new_list; + TALLOC_FREE(frame); + return NT_STATUS_OK; +} diff --git a/source4/dns_server/dnsserver_common.h b/source4/dns_server/dnsserver_common.h index becd243..ad91f61 100644 --- a/source4/dns_server/dnsserver_common.h +++ b/source4/dns_server/dnsserver_common.h @@ -26,6 +26,14 @@ uint8_t werr_to_dns_err(WERROR werr); #define DNS_ERR(err_str) WERR_DNS_ERROR_RCODE_##err_str struct ldb_message_element; +struct ldb_context; +struct dnsp_DnssrvRpcRecord; + +struct dns_server_zone { + struct dns_server_zone *prev, *next; + const char *name; + struct ldb_dn *dn; +}; WERROR dns_common_extract(const struct ldb_message_element *el, TALLOC_CTX *mem_ctx, @@ -46,5 +54,13 @@ WERROR dns_common_replace(struct ldb_context *samdb, uint32_t serial, struct dnsp_DnssrvRpcRecord *records, uint16_t rec_count); - +bool dns_name_match(const char *zone, const char *name, size_t *host_part_len); +WERROR dns_common_name2dn(struct ldb_context *samdb, + struct dns_server_zone *zones, + TALLOC_CTX *mem_ctx, + const char *name, + struct ldb_dn **_dn); +NTSTATUS dns_common_zones(struct ldb_context *samdb, + TALLOC_CTX *mem_ctx, + struct dns_server_zone **zones_ret); #endif /* __DNSSERVER_COMMON_H__ */ From cd0abf3f6e3f5f9249693e6ad0bf4990a231c8fd Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Tue, 22 Sep 2015 12:11:04 +1200 Subject: [PATCH 16/36] dns_server: Add a python module directly accessing DNS records in sam.ldb Signed-off-by: Andrew Bartlett --- python/samba/samdb.py | 10 +- source4/dns_server/pydns.c | 225 +++++++++++++++++++++++++++++++++++++++ source4/dns_server/wscript_build | 7 ++ 3 files changed, 241 insertions(+), 1 deletion(-) create mode 100644 source4/dns_server/pydns.c diff --git a/python/samba/samdb.py b/python/samba/samdb.py index 817fbdb..ca88497 100644 --- a/python/samba/samdb.py +++ b/python/samba/samdb.py @@ -27,7 +27,7 @@ import time import base64 import os -from samba import dsdb +from samba import dsdb, dsdbdns from samba.ndr import ndr_unpack, ndr_pack from samba.dcerpc import drsblobs, misc from samba.common import normalise_int32 @@ -921,3 +921,11 @@ def get_serverName(self): '''get the server DN from the rootDSE''' res = self.search(base="", scope=ldb.SCOPE_BASE, attrs=["serverName"]) return res[0]["serverName"][0] + + def dns_lookup(self, dns_name): + '''Do a DNS lookup in the database, returns the NDR database structures''' + return dsdbdns.lookup(self, dns_name) + + def dns_replace(self, dns_name, new_records): + '''Do a DNS modification on the database, sets the NDR database structures''' + return dsdbdns.replace(self, dns_name, new_records) diff --git a/source4/dns_server/pydns.c b/source4/dns_server/pydns.c new file mode 100644 index 0000000..a457295 --- /dev/null +++ b/source4/dns_server/pydns.c @@ -0,0 +1,225 @@ +/* + Unix SMB/CIFS implementation. + + Python DNS server wrapper + + Copyright (C) 2015 Andrew Bartlett + + 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 +#include "includes.h" +#include +#include +#include "dns_server/dnsserver_common.h" +#include "dsdb/samdb/samdb.h" +#include "dsdb/common/util.h" +#include "librpc/gen_ndr/ndr_dnsp.h" +#include "librpc/rpc/pyrpc_util.h" + +/* FIXME: These should be in a header file somewhere */ +#define PyErr_LDB_OR_RAISE(py_ldb, ldb) \ + if (!py_check_dcerpc_type(py_ldb, "ldb", "Ldb")) { \ + PyErr_SetString(py_ldb_get_exception(), "Ldb connection object required"); \ + return NULL; \ + } \ + ldb = pyldb_Ldb_AsLdbContext(py_ldb); + +static PyObject *py_ldb_get_exception(void) +{ + PyObject *mod = PyImport_ImportModule("ldb"); + if (mod == NULL) + return NULL; + + return PyObject_GetAttrString(mod, "LdbError"); +} + +static PyObject *py_dnsp_DnssrvRpcRecord_get_list(struct dnsp_DnssrvRpcRecord *records, + uint16_t num_records) +{ + PyObject *py_dns_list; + int i; + py_dns_list = PyList_New(num_records); + if (py_dns_list == NULL) { + return NULL; + } + for (i = 0; i < num_records; i++) { + PyObject *py_dns_record; + py_dns_record = py_return_ndr_struct("samba.dcerpc.dnsp", "DnssrvRpcRecord", records, &records[i]); + PyList_SetItem(py_dns_list, i, py_dns_record); + } + return py_dns_list; +} + +static int py_dnsp_DnssrvRpcRecord_get_array(PyObject *value, + TALLOC_CTX *mem_ctx, + struct dnsp_DnssrvRpcRecord **records, + uint16_t *num_records) +{ + int i; + struct dnsp_DnssrvRpcRecord *recs; + PY_CHECK_TYPE(&PyList_Type, value, return -1;); + recs = talloc_array(mem_ctx, struct dnsp_DnssrvRpcRecord, + PyList_GET_SIZE(value)); + if (!recs) { + PyErr_NoMemory(); + return -1; + } + for (i = 0; i < PyList_GET_SIZE(value); i++) { + bool type_correct; + PyObject *item = PyList_GET_ITEM(value, i); + type_correct = py_check_dcerpc_type(item, "samba.dcerpc.dnsp", "DnssrvRpcRecord"); + if (!type_correct) { + return -1; + } + if (talloc_reference(mem_ctx, pytalloc_get_mem_ctx(item)) == NULL) { + PyErr_NoMemory(); + return -1; + } + recs[i] = *(struct dnsp_DnssrvRpcRecord *)pytalloc_get_ptr(item); + } + *records = recs; + *num_records = PyList_GET_SIZE(value); + return 0; +} + +static PyObject *py_dsdb_dns_lookup(PyObject *self, PyObject *args) +{ + struct ldb_context *samdb; + PyObject *py_ldb; + char *dns_name; + TALLOC_CTX *frame; + NTSTATUS status; + WERROR werr; + struct dns_server_zone *zones_list; + struct ldb_dn *dn; + struct dnsp_DnssrvRpcRecord *records; + uint16_t num_records; + + if (!PyArg_ParseTuple(args, "Os", &py_ldb, &dns_name)) { + return NULL; + } + PyErr_LDB_OR_RAISE(py_ldb, samdb); + + frame = talloc_stackframe(); + + status = dns_common_zones(samdb, frame, &zones_list); + if (!NT_STATUS_IS_OK(status)) { + PyErr_SetNTSTATUS(status); + return NULL; + } + + werr = dns_common_name2dn(samdb, zones_list, frame, dns_name, &dn); + if (!W_ERROR_IS_OK(werr)) { + PyErr_SetWERROR(werr); + return NULL; + } + + werr = dns_common_lookup(samdb, + frame, + dn, + &records, + &num_records, + NULL); + if (!W_ERROR_IS_OK(werr)) { + PyErr_SetWERROR(werr); + return NULL; + } + + return py_dnsp_DnssrvRpcRecord_get_list(records, num_records); +} + +static PyObject *py_dsdb_dns_replace(PyObject *self, PyObject *args) +{ + struct ldb_context *samdb; + PyObject *py_ldb, *py_dns_records; + char *dns_name; + TALLOC_CTX *frame; + NTSTATUS status; + WERROR werr; + int ret; + struct dns_server_zone *zones_list; + struct ldb_dn *dn; + struct dnsp_DnssrvRpcRecord *records; + uint16_t num_records; + + /* + * TODO: This is a shocking abuse, but matches what the + * internal DNS server does, it should be pushed into + * dns_common_replace() + */ + static const int serial = 110; + + if (!PyArg_ParseTuple(args, "OsO", &py_ldb, &dns_name, &py_dns_records)) { + return NULL; + } + PyErr_LDB_OR_RAISE(py_ldb, samdb); + + frame = talloc_stackframe(); + + status = dns_common_zones(samdb, frame, &zones_list); + if (!NT_STATUS_IS_OK(status)) { + PyErr_SetNTSTATUS(status); + return NULL; + } + + werr = dns_common_name2dn(samdb, zones_list, frame, dns_name, &dn); + if (!W_ERROR_IS_OK(werr)) { + PyErr_SetWERROR(werr); + return NULL; + } + + ret = py_dnsp_DnssrvRpcRecord_get_array(py_dns_records, + frame, + &records, &num_records); + if (ret != 0) { + return NULL; + } + + werr = dns_common_replace(samdb, + frame, + dn, + false, /* Not adding a record */ + serial, + records, + num_records); + if (!W_ERROR_IS_OK(werr)) { + PyErr_SetWERROR(werr); + return NULL; + } + + Py_RETURN_NONE; +} + +static PyMethodDef py_dsdb_dns_methods[] = { + + { "lookup", (PyCFunction)py_dsdb_dns_lookup, + METH_VARARGS, "Get the DNS database entries for a DNS name"}, + { "replace", (PyCFunction)py_dsdb_dns_replace, + METH_VARARGS, "Replace the DNS database entries for a DNS name"}, + { NULL } +}; + +void initdsdbdns(void); + +void initdsdbdns(void) +{ + PyObject *m; + + m = Py_InitModule3("dsdbdns", py_dsdb_dns_methods, + "Python bindings for the DNS objects in the directory service databases."); + if (m == NULL) + return; +} diff --git a/source4/dns_server/wscript_build b/source4/dns_server/wscript_build index a4f0f0c..4b472d7 100644 --- a/source4/dns_server/wscript_build +++ b/source4/dns_server/wscript_build @@ -53,3 +53,10 @@ bld.SAMBA_LIBRARY('dlz_bind9_for_torture', private_library=True, deps='samba-hostconfig samdb-common gensec popt dnsserver_common', enabled=bld.AD_DC_BUILD_IS_ENABLED()) + + +bld.SAMBA_PYTHON('python_dsdbdns', + source='pydns.c', + deps='samdb pyldb-util pyrpc_util dnsserver_common', + realname='samba/dsdbdns.so' + ) From 5c682779fb160f737db85c548d781b999e615524 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Tue, 22 Sep 2015 15:25:30 +1200 Subject: [PATCH 17/36] pydsdb: Also accept ldb.MessageElement values to dsdb routines This shows the correct way to accept a value that may be a list of strings or a proper ldb.MessageElement. Andrew Bartlett Signed-off-by: Andrew Bartlett --- python/samba/dbchecker.py | 4 +- source4/dsdb/pydsdb.c | 113 +++++++++++++++++++++++++--------------------- 2 files changed, 63 insertions(+), 54 deletions(-) diff --git a/python/samba/dbchecker.py b/python/samba/dbchecker.py index 4fb9d12..69b4c61 100644 --- a/python/samba/dbchecker.py +++ b/python/samba/dbchecker.py @@ -1286,8 +1286,8 @@ def check_object(self, dn, attrs=['*']): continue if str(attrname).lower() == 'objectclass': - normalised = self.samdb.dsdb_normalise_attributes(self.samdb_schema, attrname, list(obj[attrname])) - if list(normalised) != list(obj[attrname]): + normalised = self.samdb.dsdb_normalise_attributes(self.samdb_schema, attrname, obj[attrname]) + if normalised != obj[attrname]: self.err_normalise_mismatch_replace(dn, attrname, list(obj[attrname])) error_count += 1 continue diff --git a/source4/dsdb/pydsdb.c b/source4/dsdb/pydsdb.c index 8836d85..0a11e7b 100644 --- a/source4/dsdb/pydsdb.c +++ b/source4/dsdb/pydsdb.c @@ -529,11 +529,6 @@ static PyObject *py_dsdb_DsReplicaAttribute(PyObject *self, PyObject *args) PyErr_LDB_OR_RAISE(py_ldb, ldb); - if (!PyList_Check(el_list)) { - PyErr_Format(PyExc_TypeError, "ldif_elements must be a list"); - return NULL; - } - schema = dsdb_get_schema(ldb, NULL); if (!schema) { PyErr_SetString(PyExc_RuntimeError, "Failed to find a schema from ldb"); @@ -555,32 +550,42 @@ static PyObject *py_dsdb_DsReplicaAttribute(PyObject *self, PyObject *args) return NULL; } - el = talloc_zero(tmp_ctx, struct ldb_message_element); - if (el == NULL) { - PyErr_NoMemory(); - talloc_free(tmp_ctx); - return NULL; - } - - el->name = ldap_display_name; - el->num_values = PyList_Size(el_list); + /* If we were not given an LdbMessageElement */ + if (!PyList_Check(el_list)) { + if (!py_check_dcerpc_type(el_list, "ldb", "MessageElement")) { + PyErr_SetString(py_ldb_get_exception(), + "list of strings or ldb MessageElement object required"); + return NULL; + } + el = pyldb_MessageElement_AsMessageElement(el_list); + } else { + el = talloc_zero(tmp_ctx, struct ldb_message_element); + if (el == NULL) { + PyErr_NoMemory(); + talloc_free(tmp_ctx); + return NULL; + } - el->values = talloc_array(el, struct ldb_val, el->num_values); - if (el->values == NULL) { - PyErr_NoMemory(); - talloc_free(tmp_ctx); - return NULL; - } + el->name = ldap_display_name; + el->num_values = PyList_Size(el_list); - for (i = 0; i < el->num_values; i++) { - PyObject *item = PyList_GetItem(el_list, i); - if (!PyString_Check(item)) { - PyErr_Format(PyExc_TypeError, "ldif_elements should be strings"); + el->values = talloc_array(el, struct ldb_val, el->num_values); + if (el->values == NULL) { + PyErr_NoMemory(); talloc_free(tmp_ctx); return NULL; } - el->values[i].data = (uint8_t *)PyString_AsString(item); - el->values[i].length = PyString_Size(item); + + for (i = 0; i < el->num_values; i++) { + PyObject *item = PyList_GetItem(el_list, i); + if (!PyString_Check(item)) { + PyErr_Format(PyExc_TypeError, "ldif_elements should be strings"); + talloc_free(tmp_ctx); + return NULL; + } + el->values[i].data = (uint8_t *)PyString_AsString(item); + el->values[i].length = PyString_Size(item); + } } attr = talloc_zero(tmp_ctx, struct drsuapi_DsReplicaAttribute); @@ -624,11 +629,6 @@ static PyObject *py_dsdb_normalise_attributes(PyObject *self, PyObject *args) PyErr_LDB_OR_RAISE(py_ldb, ldb); - if (!PyList_Check(el_list)) { - PyErr_Format(PyExc_TypeError, "ldif_elements must be a list"); - return NULL; - } - schema = dsdb_get_schema(ldb, NULL); if (!schema) { PyErr_SetString(PyExc_RuntimeError, "Failed to find a schema from ldb"); @@ -650,32 +650,41 @@ static PyObject *py_dsdb_normalise_attributes(PyObject *self, PyObject *args) return NULL; } - el = talloc_zero(tmp_ctx, struct ldb_message_element); - if (el == NULL) { - PyErr_NoMemory(); - talloc_free(tmp_ctx); - return NULL; - } - - el->name = ldap_display_name; - el->num_values = PyList_Size(el_list); + if (!PyList_Check(el_list)) { + if (!py_check_dcerpc_type(el_list, "ldb", "MessageElement")) { + PyErr_SetString(py_ldb_get_exception(), + "list of strings or ldb MessageElement object required"); + return NULL; + } + el = pyldb_MessageElement_AsMessageElement(el_list); + } else { + el = talloc_zero(tmp_ctx, struct ldb_message_element); + if (el == NULL) { + PyErr_NoMemory(); + talloc_free(tmp_ctx); + return NULL; + } - el->values = talloc_array(el, struct ldb_val, el->num_values); - if (el->values == NULL) { - PyErr_NoMemory(); - talloc_free(tmp_ctx); - return NULL; - } + el->name = ldap_display_name; + el->num_values = PyList_Size(el_list); - for (i = 0; i < el->num_values; i++) { - PyObject *item = PyList_GetItem(el_list, i); - if (!PyString_Check(item)) { - PyErr_Format(PyExc_TypeError, "ldif_elements should be strings"); + el->values = talloc_array(el, struct ldb_val, el->num_values); + if (el->values == NULL) { + PyErr_NoMemory(); talloc_free(tmp_ctx); return NULL; } - el->values[i].data = (uint8_t *)PyString_AsString(item); - el->values[i].length = PyString_Size(item); + + for (i = 0; i < el->num_values; i++) { + PyObject *item = PyList_GetItem(el_list, i); + if (!PyString_Check(item)) { + PyErr_Format(PyExc_TypeError, "ldif_elements should be strings"); + talloc_free(tmp_ctx); + return NULL; + } + el->values[i].data = (uint8_t *)PyString_AsString(item); + el->values[i].length = PyString_Size(item); + } } /* Normalise "objectClass" attribute if needed */ From 88597f66654a80993d12135b57357f7712f70d5a Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Tue, 22 Sep 2015 15:32:57 +1200 Subject: [PATCH 18/36] dns_server: Add python method to extract a DNS entry from a ldb.MessageElement Signed-off-by: Andrew Bartlett --- python/samba/samdb.py | 4 ++++ source4/dns_server/pydns.c | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/python/samba/samdb.py b/python/samba/samdb.py index ca88497..6454ba7 100644 --- a/python/samba/samdb.py +++ b/python/samba/samdb.py @@ -926,6 +926,10 @@ def dns_lookup(self, dns_name): '''Do a DNS lookup in the database, returns the NDR database structures''' return dsdbdns.lookup(self, dns_name) + def dns_extract(self, el): + '''Return the NDR database structures from a dnsRecord element''' + return dsdbdns.extract(el) + def dns_replace(self, dns_name, new_records): '''Do a DNS modification on the database, sets the NDR database structures''' return dsdbdns.replace(self, dns_name, new_records) diff --git a/source4/dns_server/pydns.c b/source4/dns_server/pydns.c index a457295..d4a3928 100644 --- a/source4/dns_server/pydns.c +++ b/source4/dns_server/pydns.c @@ -141,6 +141,40 @@ static PyObject *py_dsdb_dns_lookup(PyObject *self, PyObject *args) return py_dnsp_DnssrvRpcRecord_get_list(records, num_records); } +static PyObject *py_dsdb_dns_extract(PyObject *self, PyObject *args) +{ + PyObject *py_dns_el; + TALLOC_CTX *frame; + WERROR werr; + struct ldb_message_element *dns_el; + struct dnsp_DnssrvRpcRecord *records; + uint16_t num_records; + + if (!PyArg_ParseTuple(args, "O", &py_dns_el)) { + return NULL; + } + + if (!py_check_dcerpc_type(py_dns_el, "ldb", "MessageElement")) { + PyErr_SetString(py_ldb_get_exception(), + "ldb MessageElement object required"); + return NULL; + } + dns_el = pyldb_MessageElement_AsMessageElement(py_dns_el); + + frame = talloc_stackframe(); + + werr = dns_common_extract(dns_el, + frame, + &records, + &num_records); + if (!W_ERROR_IS_OK(werr)) { + PyErr_SetWERROR(werr); + return NULL; + } + + return py_dnsp_DnssrvRpcRecord_get_list(records, num_records); +} + static PyObject *py_dsdb_dns_replace(PyObject *self, PyObject *args) { struct ldb_context *samdb; @@ -209,6 +243,8 @@ static PyMethodDef py_dsdb_dns_methods[] = { METH_VARARGS, "Get the DNS database entries for a DNS name"}, { "replace", (PyCFunction)py_dsdb_dns_replace, METH_VARARGS, "Replace the DNS database entries for a DNS name"}, + { "extract", (PyCFunction)py_dsdb_dns_extract, + METH_VARARGS, "Return the DNS database entry as a python structure from an Ldb.MessageElement of type dnsRecord"}, { NULL } }; From 53787d3bd185dfab79a8fb0fd1b7ebc7475526f4 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Mon, 14 Sep 2015 15:56:52 +1200 Subject: [PATCH 19/36] samba-tool: Rework samba-tool demote command The new version of this tool now operates on RODCs, and can remove another DC that is itself offline. The --remove-other-dead-server removes as many references to the DC as possible. Signed-off-by: Andrew Bartlett --- python/samba/netcmd/domain.py | 115 +++++++++++++++----------- python/samba/remove_dc.py | 183 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 253 insertions(+), 45 deletions(-) create mode 100644 python/samba/remove_dc.py diff --git a/python/samba/netcmd/domain.py b/python/samba/netcmd/domain.py index 250fdf9..1603156 100644 --- a/python/samba/netcmd/domain.py +++ b/python/samba/netcmd/domain.py @@ -5,7 +5,7 @@ # Copyright Jelmer Vernooij 2007-2012 # Copyright Giampaolo Lauria 2011 # Copyright Matthieu Patou 2011 -# Copyright Andrew Bartlett 2008 +# Copyright Andrew Bartlett 2008-2015 # Copyright Stefan Metzmacher 2012 # # This program is free software; you can redistribute it and/or modify @@ -44,6 +44,7 @@ from samba.dcerpc import netlogon from samba.dcerpc import security from samba.dcerpc import nbt +from samba.dcerpc import misc from samba.dcerpc.samr import DOMAIN_PASSWORD_COMPLEX, DOMAIN_PASSWORD_STORE_CLEARTEXT from samba.netcmd import ( Command, @@ -58,7 +59,7 @@ from samba.drs_utils import ( sendDsReplicaSync, drsuapi_connect, drsException, sendRemoveDsServer) - +from samba import remove_dc from samba.dsdb import ( DS_DOMAIN_FUNCTION_2000, @@ -648,8 +649,10 @@ class cmd_domain_demote(Command): synopsis = "%prog [options]" takes_options = [ - Option("--server", help="DC to force replication before demote", type=str), - Option("--targetdir", help="where provision is stored", type=str), + Option("--server", help="writable DC to write demotion changes on", type=str), + Option("-H", "--URL", help="LDB URL for database or target server", type=str, + metavar="URL", dest="H"), + Option("--remove-other-dead-server", help="Dead DC to remove ALL references to (rather than this DC)", type=str), ] takes_optiongroups = { @@ -659,13 +662,24 @@ class cmd_domain_demote(Command): } def run(self, sambaopts=None, credopts=None, - versionopts=None, server=None, targetdir=None): + versionopts=None, server=None, + remove_other_dead_server=None, H=None): lp = sambaopts.get_loadparm() creds = credopts.get_credentials(lp) net = Net(creds, lp, server=credopts.ipaddress) + if remove_other_dead_server is not None: + if server is not None: + samdb = SamDB(url="ldap://%s" % server, + session_info=system_session(), + credentials=creds, lp=lp) + else: + samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp) + remove_dc.remove_dc(samdb, remove_other_dead_server) + return + netbios_name = lp.get("netbios name") - samdb = SamDB(session_info=system_session(), credentials=creds, lp=lp) + samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp) if not server: res = samdb.search(expression='(&(objectClass=computer)(serverReferenceBL=*))', attrs=["dnsHostName", "name"]) if (len(res) == 0): @@ -702,37 +716,48 @@ def run(self, sambaopts=None, credopts=None, self.errf.write("Deactivating inbound replication\n") - nmsg = ldb.Message() - nmsg.dn = msg[0].dn + if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc(): + nmsg = ldb.Message() + nmsg.dn = msg[0].dn - dsa_options |= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL - nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options") - samdb.modify(nmsg) + dsa_options |= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL + nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options") + samdb.modify(nmsg) - if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc(): self.errf.write("Asking partner server %s to synchronize from us\n" % server) for part in (samdb.get_schema_basedn(), samdb.get_config_basedn(), samdb.get_root_basedn()): + nc = drsuapi.DsReplicaObjectIdentifier() + nc.dn = str(part) + + req1 = drsuapi.DsReplicaSyncRequest1() + req1.naming_context = nc; + req1.options = drsuapi.DRSUAPI_DRS_WRIT_REP + req1.source_dsa_guid = misc.GUID(ntds_guid) + try: - sendDsReplicaSync(drsuapiBind, drsuapi_handle, ntds_guid, str(part), drsuapi.DRSUAPI_DRS_WRIT_REP) - except drsException, e: - self.errf.write( - "Error while demoting, " + drsuapiBind.DsReplicaSync(drsuapi_handle, 1, req1) + except RuntimeError as (werr, string): + if werr == 8452: #WERR_DS_DRA_NO_REPLICA + pass + else: + self.errf.write( + "Error while demoting, " "re-enabling inbound replication\n") - dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL - nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options") - samdb.modify(nmsg) - raise CommandError("Error while sending a DsReplicaSync for partion %s" % str(part), e) + dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL + nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options") + samdb.modify(nmsg) + raise CommandError("Error while sending a DsReplicaSync for partion %s" % str(part), e) try: remote_samdb = SamDB(url="ldap://%s" % server, session_info=system_session(), credentials=creds, lp=lp) self.errf.write("Changing userControl and container\n") - res = remote_samdb.search(base=str(remote_samdb.get_root_basedn()), + res = remote_samdb.search(base=str(remote_samdb.domain_dn()), expression="(&(objectClass=user)(sAMAccountName=%s$))" % netbios_name.upper(), attrs=["userAccountControl"]) @@ -758,7 +783,7 @@ def run(self, sambaopts=None, credopts=None, olduac = uac - uac ^= (UF_SERVER_TRUST_ACCOUNT|UF_TRUSTED_FOR_DELEGATION) + uac &= ~(UF_SERVER_TRUST_ACCOUNT|UF_TRUSTED_FOR_DELEGATION) uac |= UF_WORKSTATION_TRUST_ACCOUNT msg = ldb.Message() @@ -779,13 +804,12 @@ def run(self, sambaopts=None, credopts=None, raise CommandError("Error while changing account control", e) parent = msg.dn.parent() - rdn = str(res[0].dn) - rdn = string.replace(rdn, ",%s" % str(parent), "") + rdn = "%s=%s" % (res[0].dn.get_rdn_name(), res[0].dn.get_rdn_value()) # Let's move to the Computer container i = 0 - newrdn = rdn + newrdn = str(rdn) - computer_dn = ldb.Dn(remote_samdb, "CN=Computers,%s" % str(remote_samdb.get_root_basedn())) + computer_dn = ldb.Dn(remote_samdb, "CN=Computers,%s" % str(remote_samdb.domain_dn())) res = remote_samdb.search(base=computer_dn, expression=rdn, scope=ldb.SCOPE_ONELEVEL) if (len(res) != 0): @@ -843,33 +867,34 @@ def run(self, sambaopts=None, credopts=None, domain = remote_samdb.get_root_basedn() try: - sendRemoveDsServer(drsuapiBind, drsuapi_handle, server_dsa_dn, domain) - except drsException, e: - self.errf.write( - "Error while demoting, re-enabling inbound replication\n") - dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL - nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options") - samdb.modify(nmsg) + req1 = drsuapi.DsRemoveDSServerRequest1() + req1.server_dn = str(server_dsa_dn) + req1.domain_dn = str(domain) + req1.commit = 1 + + drsuapiBind.DsRemoveDSServer(drsuapi_handle, 1, req1) + except RuntimeError as (werr, string): + if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc(): + self.errf.write( + "Error while demoting, re-enabling inbound replication\n") + dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL + nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options") + samdb.modify(nmsg) msg = ldb.Message() msg.dn = newdn msg["userAccountControl"] = ldb.MessageElement("%d" % uac, - ldb.FLAG_MOD_REPLACE, - "userAccountControl") - print str(dc_dn) + ldb.FLAG_MOD_REPLACE, + "userAccountControl") remote_samdb.modify(msg) remote_samdb.rename(newdn, dc_dn) - raise CommandError("Error while sending a removeDsServer", e) + if werr == 8452: #WERR_DS_DRA_NO_REPLICA + raise CommandError("The DC %s is not present on (already removed from) the remote server: " % server_dsa_dn, e) + else: + raise CommandError("Error while sending a removeDsServer of %s: " % server_dsa_dn, e) - for s in ("CN=Enterprise,CN=Microsoft System Volumes,CN=System,CN=Configuration", - "CN=%s,CN=Microsoft System Volumes,CN=System,CN=Configuration" % lp.get("realm"), - "CN=Domain System Volumes (SYSVOL share),CN=File Replication Service,CN=System"): - try: - remote_samdb.delete(ldb.Dn(remote_samdb, - "%s,%s,%s" % (str(rdn), s, str(remote_samdb.get_root_basedn())))) - except ldb.LdbError, l: - pass + remove_dc.remove_sysvol_references(remote_samdb, rdn) for s in ("CN=Enterprise,CN=NTFRS Subscriptions", "CN=%s, CN=NTFRS Subscriptions" % lp.get("realm"), diff --git a/python/samba/remove_dc.py b/python/samba/remove_dc.py new file mode 100644 index 0000000..5bce244 --- /dev/null +++ b/python/samba/remove_dc.py @@ -0,0 +1,183 @@ +# Unix SMB/CIFS implementation. +# Copyright Matthieu Patou 2011 +# Copyright Andrew Bartlett 2008-2015 +# +# 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 . +# + +import ldb +from samba.ndr import ndr_unpack +from samba.dcerpc import misc + + +def remove_sysvol_references(samdb, rdn): + realm = samdb.domain_dns_name() + for s in ("CN=Enterprise,CN=Microsoft System Volumes,CN=System,CN=Configuration", + "CN=%s,CN=Microsoft System Volumes,CN=System,CN=Configuration" % realm, + "CN=Domain System Volumes (SYSVOL share),CN=File Replication Service,CN=System"): + try: + samdb.delete(ldb.Dn(samdb, + "%s,%s,%s" % (str(rdn), s, str(samdb.get_root_basedn())))) + except ldb.LdbError, l: + pass + +def remove_dns_references(samdb, dnsHostName): + + # Check we are using in-database DNS + zones = samdb.search(base="", scope=ldb.SCOPE_SUBTREE, + expression="(&(objectClass=dnsZone)(!(dc=RootDNSServers)))", + attrs=[], + controls=["search_options:0:2"]) + if len(zones) == 0: + return + + try: + rec = samdb.dns_lookup(dnsHostName) + except RuntimeError as (enum, estr): + if enum == 0x000025F2: #WERR_DNS_ERROR_NAME_DOES_NOT_EXIST + return + raise demoteException("lookup of %s failed: %s" % (dnsHostName, estr)) + samdb.dns_replace(dnsHostName, []) + +def offline_remove_dc(samdb, ntds_dn, + remove_computer_obj=False, + remove_server_obj=False, + remove_connection_obj=False, + seize_stale_fsmo=False, + remove_sysvol_obj=False, + remove_dns_names=False): + res = samdb.search("", + scope=ldb.SCOPE_BASE, attrs=["dsServiceName"]) + assert len(res) == 1 + my_serviceName = res[0]["dsServiceName"][0] + server_dn = ntds_dn.parent() + + # Confirm this is really a server object + msgs = samdb.search(base=server_dn, + attrs=["serverReference", "cn", + "dnsHostName"], + scope=ldb.SCOPE_BASE, + expression="(objectClass=server)") + msg = msgs[0] + dc_name = msgs[0]["cn"] + + try: + computer_dn = ldb.Dn(samdb, msgs[0]["serverReference"][0]) + except KeyError: + computer_dn = None + + try: + dnsHostName = msgs[0]["dnsHostName"][0] + except KeyError: + dnsHostName = None + + ntds_dn = msg.dn + ntds_dn.add_child(ldb.Dn(samdb, "CN=NTDS Settings")) + msgs = samdb.search(base=ntds_dn, expression="objectClass=ntdsDSA", + attrs=["objectGUID"]) + msg = msgs[0] + ntds_guid = ndr_unpack(misc.GUID, msg["objectGUID"][0]) + + if remove_connection_obj: + # Find any nTDSConnection objects with that DC as the fromServer. + # We use the GUID to avoid issues with any () chars in a server + # name. + stale_connections = samdb.search(base=samdb.get_config_basedn(), + expression="(&(objectclass=nTDSConnection)(fromServer=))" % ntds_guid) + for conn in stale_connections: + samdb.delete(conn.dn) + + if seize_stale_fsmo: + stale_fsmo_roles = samdb.search(base="", scope=ldb.SCOPE_SUBTREE, + expression="(fsmoRoleOwner=))" % ntds_guid, + controls=["search_options:0:2"]) + # Find any FSMO roles they have, give them to this server + + for role in stale_fsmo_roles: + val = str(my_serviceName) + m = ldb.Message() + m.dn = role.dn + m['value'] = ldb.MessageElement(val, ldb.FLAG_MOD_REPLACE, 'fsmoRoleOwner') + samdb.modify(mod) + + # Remove the NTDS setting tree + samdb.delete(ntds_dn, ["tree_delete:0"]) + + if remove_server_obj: + # Remove the server DN + samdb.delete(server_dn) + + if computer_dn is not None: + computer_msgs = samdb.search(base=computer_dn, + expression="objectclass=computer", + attrs=["msDS-KrbTgtLink", + "rIDSetReferences"], + scope=ldb.SCOPE_BASE) + if "rIDSetReferences" in computer_msgs[0]: + samdb.delete(computer_msgs[0]["rIDSetReferences"][0]) + if "msDS-KrbTgtLink" in computer_msgs[0]: + samdb.delete(computer_msgs[0]["msDS-KrbTgtLink"][0]) + + if remove_computer_obj: + # Delete the computer tree + samdb.delete(computer_dn, ["tree_delete:0"]) + + if "dnsHostName" in msgs[0]: + dnsHostName = msgs[0]["dnsHostName"][0] + + if dnsHostName is not None and remove_dns_names: + remove_dns_references(samdb, dnsHostName) + + if remove_sysvol_obj: + remove_sysvol_references(samdb, "CN=%s" % dc_name) + + +def remove_dc(samdb, dc_name): + + # TODO: Check if this is the last server + + samdb.transaction_start() + + msgs = samdb.search(base=samdb.get_config_basedn(), + attrs=["serverReference"], + expression="(&(objectClass=server)(cn=%s))" + % ldb.binary_encode(dc_name)) + server_dn = msgs[0].dn + + ntds_dn = ldb.Dn(samdb, "CN=NTDS Settings") + ntds_dn.add_base(msgs[0].dn) + + # Confirm this is really an ntdsDSA object + msgs = samdb.search(base=ntds_dn, attrs=[], scope=ldb.SCOPE_BASE, + expression="(objectClass=ntdsdsa)") + + offline_remove_dc(samdb, msgs[0].dn, + remove_computer_obj=True, + remove_server_obj=True, + remove_connection_obj=True, + seize_stale_fsmo=True, + remove_sysvol_obj=True, + remove_dns_names=True) + + samdb.transaction_commit() + + + +def offline_remove_dc_RemoveDsServer(samdb, ntds_dn): + + samdb.start_transaction() + + offline_remove_dc(samdb, ntds_dn) + + samdb.commit_transaction() From 959f8fca0c82d6c60747481d8dffb16c67d9f7bb Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Tue, 22 Sep 2015 15:34:10 +1200 Subject: [PATCH 20/36] selftest: Add testing of the KCC and remove_dc code in combination We check that the KCC operates as we remove DCs one at at a time Signed-off-by: Andrew Bartlett --- python/samba/tests/kcc/ldif_import_export.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/python/samba/tests/kcc/ldif_import_export.py b/python/samba/tests/kcc/ldif_import_export.py index f3352e2..ba7d1b9 100644 --- a/python/samba/tests/kcc/ldif_import_export.py +++ b/python/samba/tests/kcc/ldif_import_export.py @@ -35,6 +35,7 @@ from samba.param import LoadParm from samba.credentials import Credentials from samba.samdb import SamDB +from samba import remove_dc unix_now = int(time.time()) @@ -192,6 +193,26 @@ def test_verify(self): attempt_live_connections=False) self.remove_files(tmpdb) + def test_verify_with_dc_removal(self): + """Check that the KCC generates graphs that pass its own verify + option even as we remove DCs + """ + my_kcc = self._get_kcc('test-verify', verify=True) + tmpdb = os.path.join(self.tempdir, 'verify-tmpdb') + forced_dsa = "CN=WIN10,CN=Servers,CN=Site-5,CN=Sites,CN=Configuration,DC=ad,DC=samba,DC=example,DC=com" + my_kcc.import_ldif(tmpdb, self.lp, self.creds, MULTISITE_LDIF, + forced_local_dsa=forced_dsa) + + for dsa, site in MULTISITE_LDIF_DSAS: + if dsa == forced_dsa: + continue + rdn = ldb.Dn(my_kcc.samdb, dsa).get_rdn_value() + remove_dc.remove_dc(my_kcc.samdb, rdn) + my_kcc.run("ldap://%s" % tmpdb, + self.lp, self.creds, + attempt_live_connections=False) + self.remove_files(tmpdb) + def test_dotfiles(self): """Check that KCC writes dot_files when asked. """ From fee163fe93dc43d977816855a4a4582f4b698eb7 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Tue, 22 Sep 2015 15:39:19 +1200 Subject: [PATCH 21/36] selftest: Reorder tests.py to ensure that demote, then dbcheck run last. Signed-off-by: Andrew Bartlett --- source4/selftest/tests.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/source4/selftest/tests.py b/source4/selftest/tests.py index 6c72c34..17bd8c9 100755 --- a/source4/selftest/tests.py +++ b/source4/selftest/tests.py @@ -535,7 +535,6 @@ def planoldpythontestsuite(env, module, name=None, extra_path=[], environ={}, ex # Test renaming the DC plantestsuite("samba4.blackbox.renamedc.sh", "none", ["PYTHON=%s" % python, os.path.join(bbdir, "renamedc.sh"), '$PREFIX/provision']) -# Demote the vampire DC, it must be the last test on the VAMPIRE DC for env in ['vampire_dc', 'promoted_dc']: # DRS python tests @@ -563,7 +562,6 @@ def planoldpythontestsuite(env, module, name=None, extra_path=[], environ={}, ex environ={'DC1': "$DC_SERVER", 'DC2': '$%s_SERVER' % env.upper()}, extra_args=['-U$DOMAIN/$DC_USERNAME%$DC_PASSWORD']) - plantestsuite("samba4.blackbox.samba_tool_demote(%s)" % env, env, [os.path.join(samba4srcdir, "utils/tests/test_demote.sh"), '$SERVER', '$SERVER_IP', '$USERNAME', '$PASSWORD', '$DOMAIN', '$DC_SERVER', '$PREFIX/%s' % env, smbclient4]) for env in ["ad_dc_ntvfs", "s4member", "rodc", "promoted_dc", "ad_dc", "ad_member"]: plantestsuite("samba.blackbox.wbinfo(%s:local)" % env, "%s:local" % env, [os.path.join(samba4srcdir, "../nsswitch/tests/test_wbinfo.sh"), '$DOMAIN', '$DC_USERNAME', '$DC_PASSWORD', env]) @@ -598,11 +596,6 @@ def planoldpythontestsuite(env, module, name=None, extra_path=[], environ={}, ex '--option=torture:krb5-hostname=testallowed'] + extra_options, "samba4.krb5.kdc with account ALLOWED permission to replicate to an RODC") -# TODO: Verifying the databases really should be a part of the -# environment teardown. -# check the databases are all OK. PLEASE LEAVE THIS AS THE LAST TEST -for env in ["ad_dc_ntvfs", "ad_dc", "fl2000dc", "fl2003dc", "fl2008r2dc", 'vampire_dc', 'promoted_dc']: - plantestsuite("samba4.blackbox.dbcheck(%s)" % env, env + ":local" , ["PYTHON=%s" % python, os.path.join(bbdir, "dbcheck.sh"), '$PREFIX/provision', configuration]) for env in [ 'vampire_dc', @@ -615,3 +608,13 @@ def planoldpythontestsuite(env, module, name=None, extra_path=[], environ={}, ex }, extra_path=[os.path.join(srcdir(), "samba/python"), ] ) + +# Demote the vampire DC, it must be the last test each DC, before the dbcheck +for env in ['vampire_dc', 'promoted_dc']: + plantestsuite("samba4.blackbox.samba_tool_demote(%s)" % env, env, [os.path.join(samba4srcdir, "utils/tests/test_demote.sh"), '$SERVER', '$SERVER_IP', '$USERNAME', '$PASSWORD', '$DOMAIN', '$DC_SERVER', '$PREFIX/%s' % env, smbclient4]) + +# TODO: Verifying the databases really should be a part of the +# environment teardown. +# check the databases are all OK. PLEASE LEAVE THIS AS THE LAST TEST +for env in ["ad_dc_ntvfs", "ad_dc", "fl2000dc", "fl2003dc", "fl2008r2dc", 'vampire_dc', 'promoted_dc']: + plantestsuite("samba4.blackbox.dbcheck(%s)" % env, env + ":local" , ["PYTHON=%s" % python, os.path.join(bbdir, "dbcheck.sh"), '$PREFIX/provision', configuration]) From ad2c13dabf51bc1fb12db2dba9ce32e398c26305 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Tue, 22 Sep 2015 15:40:00 +1200 Subject: [PATCH 22/36] selftest: Run demote test against the RODC environment also --- source4/selftest/tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source4/selftest/tests.py b/source4/selftest/tests.py index 17bd8c9..acacadb 100755 --- a/source4/selftest/tests.py +++ b/source4/selftest/tests.py @@ -610,7 +610,7 @@ def planoldpythontestsuite(env, module, name=None, extra_path=[], environ={}, ex ) # Demote the vampire DC, it must be the last test each DC, before the dbcheck -for env in ['vampire_dc', 'promoted_dc']: +for env in ['vampire_dc', 'promoted_dc', 'rodc']: plantestsuite("samba4.blackbox.samba_tool_demote(%s)" % env, env, [os.path.join(samba4srcdir, "utils/tests/test_demote.sh"), '$SERVER', '$SERVER_IP', '$USERNAME', '$PASSWORD', '$DOMAIN', '$DC_SERVER', '$PREFIX/%s' % env, smbclient4]) # TODO: Verifying the databases really should be a part of the From 352c7f57d0e31ab6643ebacf5b1679112d6888d4 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Tue, 22 Sep 2015 15:51:33 +1200 Subject: [PATCH 23/36] selftest: Make it clear that the first argument to KCC.run() is unused This is unused because we have already provided a database via import_ldif Signed-off-by: Andrew Bartlett --- python/samba/tests/kcc/ldif_import_export.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/python/samba/tests/kcc/ldif_import_export.py b/python/samba/tests/kcc/ldif_import_export.py index ba7d1b9..a9e11cd 100644 --- a/python/samba/tests/kcc/ldif_import_export.py +++ b/python/samba/tests/kcc/ldif_import_export.py @@ -188,7 +188,7 @@ def test_verify(self): tmpdb = os.path.join(self.tempdir, 'verify-tmpdb') my_kcc.import_ldif(tmpdb, self.lp, self.creds, MULTISITE_LDIF) - my_kcc.run("ldap://%s" % tmpdb, + my_kcc.run(None, self.lp, self.creds, attempt_live_connections=False) self.remove_files(tmpdb) @@ -208,7 +208,7 @@ def test_verify_with_dc_removal(self): continue rdn = ldb.Dn(my_kcc.samdb, dsa).get_rdn_value() remove_dc.remove_dc(my_kcc.samdb, rdn) - my_kcc.run("ldap://%s" % tmpdb, + my_kcc.run(None, self.lp, self.creds, attempt_live_connections=False) self.remove_files(tmpdb) @@ -220,7 +220,7 @@ def test_dotfiles(self): tmpdb = os.path.join(self.tempdir, 'dotfile-tmpdb') files = [tmpdb] my_kcc.import_ldif(tmpdb, self.lp, self.creds, MULTISITE_LDIF) - my_kcc.run("ldap://%s" % tmpdb, + my_kcc.run(None, self.lp, self.creds, attempt_live_connections=False) From 7854bfc40d06bf77260c601c4d60b18abbe5fb61 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Thu, 24 Sep 2015 14:07:51 +1200 Subject: [PATCH 24/36] samba-tool domain demote: Rework to allow cleanup of partial demotion, catch more errors Signed-off-by: Andrew Bartlett --- python/samba/netcmd/domain.py | 5 +- python/samba/remove_dc.py | 159 ++++++++++++++++++++++++++++-------------- 2 files changed, 112 insertions(+), 52 deletions(-) diff --git a/python/samba/netcmd/domain.py b/python/samba/netcmd/domain.py index 1603156..7285ec1 100644 --- a/python/samba/netcmd/domain.py +++ b/python/samba/netcmd/domain.py @@ -675,7 +675,10 @@ def run(self, sambaopts=None, credopts=None, credentials=creds, lp=lp) else: samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp) - remove_dc.remove_dc(samdb, remove_other_dead_server) + try: + remove_dc.remove_dc(samdb, remove_other_dead_server) + except remove_dc.demoteException as err: + raise CommandError("Demote failed: %s" % err) return netbios_name = lp.get("netbios name") diff --git a/python/samba/remove_dc.py b/python/samba/remove_dc.py index 5bce244..9250d52 100644 --- a/python/samba/remove_dc.py +++ b/python/samba/remove_dc.py @@ -17,9 +17,19 @@ # import ldb +from ldb import LdbError from samba.ndr import ndr_unpack from samba.dcerpc import misc +class demoteException(Exception): + """Base element for demote errors""" + + def __init__(self, value): + self.value = value + + def __str__(self): + return "demoteException: " + self.value + def remove_sysvol_references(samdb, rdn): realm = samdb.domain_dns_name() @@ -50,18 +60,15 @@ def remove_dns_references(samdb, dnsHostName): raise demoteException("lookup of %s failed: %s" % (dnsHostName, estr)) samdb.dns_replace(dnsHostName, []) -def offline_remove_dc(samdb, ntds_dn, - remove_computer_obj=False, - remove_server_obj=False, - remove_connection_obj=False, - seize_stale_fsmo=False, - remove_sysvol_obj=False, - remove_dns_names=False): +def offline_remove_server(samdb, server_dn, + remove_computer_obj=False, + remove_server_obj=False, + remove_sysvol_obj=False, + remove_dns_names=False): res = samdb.search("", scope=ldb.SCOPE_BASE, attrs=["dsServiceName"]) assert len(res) == 1 my_serviceName = res[0]["dsServiceName"][0] - server_dn = ntds_dn.parent() # Confirm this is really a server object msgs = samdb.search(base=server_dn, @@ -82,38 +89,6 @@ def offline_remove_dc(samdb, ntds_dn, except KeyError: dnsHostName = None - ntds_dn = msg.dn - ntds_dn.add_child(ldb.Dn(samdb, "CN=NTDS Settings")) - msgs = samdb.search(base=ntds_dn, expression="objectClass=ntdsDSA", - attrs=["objectGUID"]) - msg = msgs[0] - ntds_guid = ndr_unpack(misc.GUID, msg["objectGUID"][0]) - - if remove_connection_obj: - # Find any nTDSConnection objects with that DC as the fromServer. - # We use the GUID to avoid issues with any () chars in a server - # name. - stale_connections = samdb.search(base=samdb.get_config_basedn(), - expression="(&(objectclass=nTDSConnection)(fromServer=))" % ntds_guid) - for conn in stale_connections: - samdb.delete(conn.dn) - - if seize_stale_fsmo: - stale_fsmo_roles = samdb.search(base="", scope=ldb.SCOPE_SUBTREE, - expression="(fsmoRoleOwner=))" % ntds_guid, - controls=["search_options:0:2"]) - # Find any FSMO roles they have, give them to this server - - for role in stale_fsmo_roles: - val = str(my_serviceName) - m = ldb.Message() - m.dn = role.dn - m['value'] = ldb.MessageElement(val, ldb.FLAG_MOD_REPLACE, 'fsmoRoleOwner') - samdb.modify(mod) - - # Remove the NTDS setting tree - samdb.delete(ntds_dn, ["tree_delete:0"]) - if remove_server_obj: # Remove the server DN samdb.delete(server_dn) @@ -142,6 +117,72 @@ def offline_remove_dc(samdb, ntds_dn, if remove_sysvol_obj: remove_sysvol_references(samdb, "CN=%s" % dc_name) +def offline_remove_ntds_dc(samdb, ntds_dn, + remove_computer_obj=False, + remove_server_obj=False, + remove_connection_obj=False, + seize_stale_fsmo=False, + remove_sysvol_obj=False, + remove_dns_names=False): + res = samdb.search("", + scope=ldb.SCOPE_BASE, attrs=["dsServiceName"]) + assert len(res) == 1 + my_serviceName = res[0]["dsServiceName"][0] + server_dn = ntds_dn.parent() + + try: + msgs = samdb.search(base=ntds_dn, expression="objectClass=ntdsDSA", + attrs=["objectGUID"], scope=ldb.SCOPE_BASE) + except LdbError as (enum, estr): + if enum == ldb.ERR_NO_SUCH_OBJECT: + raise demoteException("Given DN %s doesn't exist" % ntds_dn) + else: + raise + if (len(msgs) == 0): + raise demoteException("%s is not an ntdsda in %s" + % (ntds_dn, samdb.domain_dns_name())) + + msg = msgs[0] + if (msg.dn.get_rdn_name() != "CN" or + msg.dn.get_rdn_value() != "NTDS Settings"): + raise demoteException("Given DN (%s) wasn't the NTDS Settings DN" % ntds_dn) + + ntds_guid = ndr_unpack(misc.GUID, msg["objectGUID"][0]) + + if remove_connection_obj: + # Find any nTDSConnection objects with that DC as the fromServer. + # We use the GUID to avoid issues with any () chars in a server + # name. + stale_connections = samdb.search(base=samdb.get_config_basedn(), + expression="(&(objectclass=nTDSConnection)(fromServer=))" % ntds_guid) + for conn in stale_connections: + samdb.delete(conn.dn) + + if seize_stale_fsmo: + stale_fsmo_roles = samdb.search(base="", scope=ldb.SCOPE_SUBTREE, + expression="(fsmoRoleOwner=))" % ntds_guid, + controls=["search_options:0:2"]) + # Find any FSMO roles they have, give them to this server + + for role in stale_fsmo_roles: + val = str(my_serviceName) + m = ldb.Message() + m.dn = role.dn + m['value'] = ldb.MessageElement(val, ldb.FLAG_MOD_REPLACE, 'fsmoRoleOwner') + samdb.modify(m) + + # Remove the NTDS setting tree + try: + samdb.delete(ntds_dn, ["tree_delete:0"]) + except LdbError as (enum, estr): + raise demoteException("Failed to remove the DCs NTDS DSA object: %s" % estr) + + offline_remove_server(samdb, server_dn, + remove_computer_obj=remove_computer_obj, + remove_server_obj=remove_server_obj, + remove_sysvol_obj=remove_sysvol_obj, + remove_dns_names=remove_dns_names) + def remove_dc(samdb, dc_name): @@ -152,23 +193,39 @@ def remove_dc(samdb, dc_name): msgs = samdb.search(base=samdb.get_config_basedn(), attrs=["serverReference"], expression="(&(objectClass=server)(cn=%s))" - % ldb.binary_encode(dc_name)) + % ldb.binary_encode(dc_name)) + if (len(msgs) == 0): + raise demoteException("%s is not an AD DC in %s" + % (dc_name, samdb.domain_dns_name())) server_dn = msgs[0].dn ntds_dn = ldb.Dn(samdb, "CN=NTDS Settings") ntds_dn.add_base(msgs[0].dn) # Confirm this is really an ntdsDSA object - msgs = samdb.search(base=ntds_dn, attrs=[], scope=ldb.SCOPE_BASE, - expression="(objectClass=ntdsdsa)") + try: + msgs = samdb.search(base=ntds_dn, attrs=[], scope=ldb.SCOPE_BASE, + expression="(objectClass=ntdsdsa)") + except LdbError as (enum, estr): + if enum == ldb.ERR_NO_SUCH_OBJECT: + offline_remove_server(samdb, msgs[0].dn, + remove_computer_obj=True, + remove_server_obj=True, + remove_sysvol_obj=True, + remove_dns_names=True) + + samdb.transaction_commit() + return + else: + pass - offline_remove_dc(samdb, msgs[0].dn, - remove_computer_obj=True, - remove_server_obj=True, - remove_connection_obj=True, - seize_stale_fsmo=True, - remove_sysvol_obj=True, - remove_dns_names=True) + offline_remove_ntds_dc(samdb, msgs[0].dn, + remove_computer_obj=True, + remove_server_obj=True, + remove_connection_obj=True, + seize_stale_fsmo=True, + remove_sysvol_obj=True, + remove_dns_names=True) samdb.transaction_commit() @@ -178,6 +235,6 @@ def offline_remove_dc_RemoveDsServer(samdb, ntds_dn): samdb.start_transaction() - offline_remove_dc(samdb, ntds_dn) + offline_remove_ntds_dc(samdb, ntds_dn) samdb.commit_transaction() From 22d7d019f5a34d98b6f37474ab2482c137576c44 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Tue, 13 Oct 2015 15:23:55 +1300 Subject: [PATCH 25/36] selftest: Run samba-tool domain demote while we have a clone of the DB handy This avoids needing to run the demote on the main replicated DB of the selftest system Signed-off-by: Andrew Bartlett --- python/samba/tests/blackbox/samba_tool_drs.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/python/samba/tests/blackbox/samba_tool_drs.py b/python/samba/tests/blackbox/samba_tool_drs.py index 1cc2b50..d6d3311 100644 --- a/python/samba/tests/blackbox/samba_tool_drs.py +++ b/python/samba/tests/blackbox/samba_tool_drs.py @@ -161,6 +161,11 @@ def test_samba_tool_drs_clone_dc_secrets(self): # The clone should pretend to be the source server self.assertEqual(ds_name, server_ds_name) self.assertEqual(ldap_service_name, server_ldap_service_name) + + # While we have this cloned, try demoting the other server on the clone + out = self.check_output("samba-tool domain demote --remove-other-dead-server=%s -H %s/private/sam.ldb" + % (self.dc2, + self.tempdir)) shutil.rmtree(os.path.join(self.tempdir, "private")) shutil.rmtree(os.path.join(self.tempdir, "etc")) shutil.rmtree(os.path.join(self.tempdir, "msg.lock")) From ba47079f11b9fec4d464a7c964efa2080c22c37a Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Tue, 13 Oct 2015 15:26:20 +1300 Subject: [PATCH 26/36] samba-tool domain demote: Refuse to remove ourself This ensures that a different server is the one being demoted from the local database Signed-off-by: Andrew Bartlett --- python/samba/remove_dc.py | 8 ++++++-- python/samba/tests/blackbox/samba_tool_drs.py | 7 +++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/python/samba/remove_dc.py b/python/samba/remove_dc.py index 9250d52..c9ee050 100644 --- a/python/samba/remove_dc.py +++ b/python/samba/remove_dc.py @@ -127,9 +127,12 @@ def offline_remove_ntds_dc(samdb, ntds_dn, res = samdb.search("", scope=ldb.SCOPE_BASE, attrs=["dsServiceName"]) assert len(res) == 1 - my_serviceName = res[0]["dsServiceName"][0] + my_serviceName = ldb.Dn(samdb, res[0]["dsServiceName"][0]) server_dn = ntds_dn.parent() + if my_serviceName == ntds_dn: + raise demoteException("Refusing to demote our own DSA: %s " % my_serviceName) + try: msgs = samdb.search(base=ntds_dn, expression="objectClass=ntdsDSA", attrs=["objectGUID"], scope=ldb.SCOPE_BASE) @@ -186,7 +189,8 @@ def offline_remove_ntds_dc(samdb, ntds_dn, def remove_dc(samdb, dc_name): - # TODO: Check if this is the last server + # TODO: Check if this is the last server (covered a mostly by + # refusing to remove our own name) samdb.transaction_start() diff --git a/python/samba/tests/blackbox/samba_tool_drs.py b/python/samba/tests/blackbox/samba_tool_drs.py index d6d3311..daab42f 100644 --- a/python/samba/tests/blackbox/samba_tool_drs.py +++ b/python/samba/tests/blackbox/samba_tool_drs.py @@ -162,6 +162,13 @@ def test_samba_tool_drs_clone_dc_secrets(self): self.assertEqual(ds_name, server_ds_name) self.assertEqual(ldap_service_name, server_ldap_service_name) + def demote_self(): + # While we have this cloned, try demoting the other server on the clone + out = self.check_output("samba-tool domain demote --remove-other-dead-server=%s -H %s/private/sam.ldb" + % (self.dc1, + self.tempdir)) + self.assertRaises(samba.tests.BlackboxProcessError, demote_self) + # While we have this cloned, try demoting the other server on the clone out = self.check_output("samba-tool domain demote --remove-other-dead-server=%s -H %s/private/sam.ldb" % (self.dc2, From f2639ed365411d7e1178658b60b776a28c7f37df Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Tue, 13 Oct 2015 16:41:44 +1300 Subject: [PATCH 27/36] selftest: Add tests confirming the demote actually removes objects Signed-off-by: Andrew Bartlett --- python/samba/tests/blackbox/samba_tool_drs.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/python/samba/tests/blackbox/samba_tool_drs.py b/python/samba/tests/blackbox/samba_tool_drs.py index daab42f..1d5f257 100644 --- a/python/samba/tests/blackbox/samba_tool_drs.py +++ b/python/samba/tests/blackbox/samba_tool_drs.py @@ -20,6 +20,7 @@ import samba.tests import shutil import os +import ldb class SambaToolDrsTests(samba.tests.BlackboxTestCase): """Blackbox test case for samba-tool drs.""" @@ -149,6 +150,7 @@ def test_samba_tool_drs_clone_dc_secrets(self): self.tempdir)) ldb_rootdse = self._get_rootDSE("tdb://" + os.path.join(self.tempdir, "private", "sam.ldb"), ldap_only=False) nc_name = ldb_rootdse["defaultNamingContext"] + config_nc_name = ldb_rootdse["configurationNamingContext"] ds_name = ldb_rootdse["dsServiceName"] ldap_service_name = str(server_rootdse["ldapServiceName"][0]) @@ -162,6 +164,9 @@ def test_samba_tool_drs_clone_dc_secrets(self): self.assertEqual(ds_name, server_ds_name) self.assertEqual(ldap_service_name, server_ldap_service_name) + server_dn = samdb.searchone("serverReferenceBL", "cn=%s,ou=domain controllers,%s" % (self.dc2, server_nc_name)) + ntds_guid = samdb.searchone("objectGUID", "cn=ntds settings,%s" % server_dn) + def demote_self(): # While we have this cloned, try demoting the other server on the clone out = self.check_output("samba-tool domain demote --remove-other-dead-server=%s -H %s/private/sam.ldb" @@ -173,6 +178,20 @@ def demote_self(): out = self.check_output("samba-tool domain demote --remove-other-dead-server=%s -H %s/private/sam.ldb" % (self.dc2, self.tempdir)) + + # Check some of the objects that should have been removed + def check_machine_obj(): + samdb.searchone("CN", "cn=%s,ou=domain controllers,%s" % (self.dc2, server_nc_name)) + self.assertRaises(ldb.LdbError, check_machine_obj) + + def check_server_obj(): + samdb.searchone("CN", server_dn) + self.assertRaises(ldb.LdbError, check_server_obj) + + def check_ntds_guid(): + samdb.searchone("CN", "" % ntds_guid) + self.assertRaises(ldb.LdbError, check_ntds_guid) + shutil.rmtree(os.path.join(self.tempdir, "private")) shutil.rmtree(os.path.join(self.tempdir, "etc")) shutil.rmtree(os.path.join(self.tempdir, "msg.lock")) From 587cc61e8288072d759e665280e9939c8b1082dd Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Wed, 14 Oct 2015 11:58:22 +1300 Subject: [PATCH 28/36] dns_server: Give DNS_ERR(SERVER_FAILURE) on ldb errors LDB should, when working correctly, give either OK or NO_SUCH_OBJECT, other errors are server failures, not invalid names. Signed-off-by: Andrew Bartlett --- source4/dns_server/dnsserver_common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source4/dns_server/dnsserver_common.c b/source4/dns_server/dnsserver_common.c index 7199ef7..0c561fd 100644 --- a/source4/dns_server/dnsserver_common.c +++ b/source4/dns_server/dnsserver_common.c @@ -138,7 +138,7 @@ WERROR dns_common_lookup(struct ldb_context *samdb, if (ret != LDB_SUCCESS) { /* TODO: we need to check if there's a glue record we need to * create a referral to */ - return DNS_ERR(NAME_ERROR); + return DNS_ERR(SERVER_FAILURE); } if (tombstoned != NULL) { From 635968768f315d37af1dbb344eafcd754eacc695 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Wed, 14 Oct 2015 11:59:26 +1300 Subject: [PATCH 29/36] dns_server: Give WERR_DNS_ERROR_NAME_DOES_NOT_EXIST on empty records When not looking for tombstones, a record without a dnsRecord value may as well not be present, so just return WERR_DNS_ERROR_NAME_DOES_NOT_EXIST Signed-off-by: Andrew Bartlett --- source4/dns_server/dnsserver_common.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/source4/dns_server/dnsserver_common.c b/source4/dns_server/dnsserver_common.c index 0c561fd..0cc16fc 100644 --- a/source4/dns_server/dnsserver_common.c +++ b/source4/dns_server/dnsserver_common.c @@ -149,15 +149,15 @@ WERROR dns_common_lookup(struct ldb_context *samdb, el = ldb_msg_find_element(msg, "dnsRecord"); if (el == NULL) { TALLOC_FREE(msg); + /* + * records produced by older Samba releases + * keep dnsNode objects without dnsRecord and + * without setting dNSTombstoned=TRUE. + * + * We just pretend they're tombstones. + */ if (tombstoned != NULL) { struct dnsp_DnssrvRpcRecord *recs; - /* - * records produced by older Samba releases - * keep dnsNode objects without dnsRecord and - * without setting dNSTombstoned=TRUE. - * - * We just pretend they're tombstones. - */ recs = talloc_array(mem_ctx, struct dnsp_DnssrvRpcRecord, 1); @@ -179,8 +179,14 @@ WERROR dns_common_lookup(struct ldb_context *samdb, *records = recs; *num_records = 1; return WERR_OK; + } else { + /* + * Because we are not looking for a tombstone + * in this codepath, we just pretend it does + * not exist at all. + */ + return WERR_DNS_ERROR_NAME_DOES_NOT_EXIST; } - return DNS_ERR(NAME_ERROR); } werr = dns_common_extract(el, mem_ctx, records, num_records); From 33ac69af234b6ec276645ce7380482bb78bd1852 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Wed, 14 Oct 2015 13:52:45 +1300 Subject: [PATCH 30/36] samba-tool domain demote: Use correct DNs to delete and use dn.add_base/dn.add_child This is done primarilly to set the pattern that we should manipulate ldb.Dn values with the helper routines, not just by concatonation via format strings. We also restrict our exception hadling to only the expected errors, not all errors. Andrew Bartlett Signed-off-by: Andrew Bartlett --- python/samba/remove_dc.py | 48 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 39 insertions(+), 9 deletions(-) diff --git a/python/samba/remove_dc.py b/python/samba/remove_dc.py index c9ee050..af70f40 100644 --- a/python/samba/remove_dc.py +++ b/python/samba/remove_dc.py @@ -31,16 +31,46 @@ def __str__(self): return "demoteException: " + self.value -def remove_sysvol_references(samdb, rdn): +def remove_sysvol_references(samdb, dc_name): realm = samdb.domain_dns_name() - for s in ("CN=Enterprise,CN=Microsoft System Volumes,CN=System,CN=Configuration", - "CN=%s,CN=Microsoft System Volumes,CN=System,CN=Configuration" % realm, - "CN=Domain System Volumes (SYSVOL share),CN=File Replication Service,CN=System"): + for s in ("CN=Enterprise,CN=Microsoft System Volumes,CN=System", + "CN=%s,CN=Microsoft System Volumes,CN=System" % realm): try: - samdb.delete(ldb.Dn(samdb, - "%s,%s,%s" % (str(rdn), s, str(samdb.get_root_basedn())))) - except ldb.LdbError, l: + dn = ldb.Dn(samdb, s) + + # This is verbose, but it is the safe, escape-proof way + # to add a base and add an arbitrary RDN. + if dn.add_base(samdb.get_config_basedn()) == False: + raise demoteException("Failed constructing DN %s by adding base %s" \ + % (dn, samdb.get_config_basedn())) + if dn.add_child("CN=X") == False: + raise demoteException("Failed constructing DN %s by adding child CN=X"\ + % (dn)) + dn.set_component(0, "CN", dc_name) + samdb.delete(dn) + except ldb.LdbError as (enum, estr): + if enum == ldb.ERR_NO_SUCH_OBJECT: + pass + else: + raise + + try: + # This is verbose, but it is the safe, escape-proof way + # to add a base and add an arbitrary RDN. + dn = ldb.Dn(samdb, "CN=Domain System Volumes (SYSVOL share),CN=File Replication Service,CN=System") + if dn.add_base(samdb.get_default_basedn()) == False: + raise demoteException("Failed constructing DN %s by adding base" % \ + (dn, samdb.get_default_basedn())) + if dn.add_child("CN=X") == False: + raise demoteException("Failed constructing DN %s by adding child %s"\ + % (dn, rdn)) + dn.set_component(0, "CN", dc_name) + samdb.delete(dn) + except ldb.LdbError as (enum, estr): + if enum == ldb.ERR_NO_SUCH_OBJECT: pass + else: + raise def remove_dns_references(samdb, dnsHostName): @@ -77,7 +107,7 @@ def offline_remove_server(samdb, server_dn, scope=ldb.SCOPE_BASE, expression="(objectClass=server)") msg = msgs[0] - dc_name = msgs[0]["cn"] + dc_name = str(msgs[0]["cn"][0]) try: computer_dn = ldb.Dn(samdb, msgs[0]["serverReference"][0]) @@ -115,7 +145,7 @@ def offline_remove_server(samdb, server_dn, remove_dns_references(samdb, dnsHostName) if remove_sysvol_obj: - remove_sysvol_references(samdb, "CN=%s" % dc_name) + remove_sysvol_references(samdb, dc_name) def offline_remove_ntds_dc(samdb, ntds_dn, remove_computer_obj=False, From e13da105c4d817704a6293e4b9541f86308bf92b Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Wed, 14 Oct 2015 16:56:41 +1300 Subject: [PATCH 31/36] pydns: Add replace_by_dn() This allows us to find a DNS record by searching LDB and unpacking the dnsRecord but replace the record using the common code that will create a tombstone Signed-off-by: Andrew Bartlett --- python/samba/samdb.py | 6 ++++- source4/dns_server/pydns.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 1 deletion(-) diff --git a/python/samba/samdb.py b/python/samba/samdb.py index 6454ba7..103bd2f 100644 --- a/python/samba/samdb.py +++ b/python/samba/samdb.py @@ -931,5 +931,9 @@ def dns_extract(self, el): return dsdbdns.extract(el) def dns_replace(self, dns_name, new_records): - '''Do a DNS modification on the database, sets the NDR database structures''' + '''Do a DNS modification on the database, sets the NDR database structures on a DNS name''' return dsdbdns.replace(self, dns_name, new_records) + + def dns_replace_by_dn(self, dn, new_records): + '''Do a DNS modification on the database, sets the NDR database structures on a LDB DN''' + return dsdbdns.replace_by_dn(self, dn, new_records) diff --git a/source4/dns_server/pydns.c b/source4/dns_server/pydns.c index d4a3928..30f16a5 100644 --- a/source4/dns_server/pydns.c +++ b/source4/dns_server/pydns.c @@ -37,6 +37,13 @@ } \ ldb = pyldb_Ldb_AsLdbContext(py_ldb); +#define PyErr_LDB_DN_OR_RAISE(py_ldb_dn, dn) \ + if (!py_check_dcerpc_type(py_ldb_dn, "ldb", "Dn")) { \ + PyErr_SetString(py_ldb_get_exception(), "ldb Dn object required"); \ + return NULL; \ + } \ + dn = pyldb_Dn_AsDn(py_ldb_dn); + static PyObject *py_ldb_get_exception(void) { PyObject *mod = PyImport_ImportModule("ldb"); @@ -237,12 +244,63 @@ static PyObject *py_dsdb_dns_replace(PyObject *self, PyObject *args) Py_RETURN_NONE; } +static PyObject *py_dsdb_dns_replace_by_dn(PyObject *self, PyObject *args) +{ + struct ldb_context *samdb; + PyObject *py_ldb, *py_dn, *py_dns_records; + TALLOC_CTX *frame; + WERROR werr; + int ret; + struct ldb_dn *dn; + struct dnsp_DnssrvRpcRecord *records; + uint16_t num_records; + + /* + * TODO: This is a shocking abuse, but matches what the + * internal DNS server does, it should be pushed into + * dns_common_replace() + */ + static const int serial = 110; + + if (!PyArg_ParseTuple(args, "OOO", &py_ldb, &py_dn, &py_dns_records)) { + return NULL; + } + PyErr_LDB_OR_RAISE(py_ldb, samdb); + + PyErr_LDB_DN_OR_RAISE(py_dn, dn); + + frame = talloc_stackframe(); + + ret = py_dnsp_DnssrvRpcRecord_get_array(py_dns_records, + frame, + &records, &num_records); + if (ret != 0) { + return NULL; + } + + werr = dns_common_replace(samdb, + frame, + dn, + false, /* Not adding a record */ + serial, + records, + num_records); + if (!W_ERROR_IS_OK(werr)) { + PyErr_SetWERROR(werr); + return NULL; + } + + Py_RETURN_NONE; +} + static PyMethodDef py_dsdb_dns_methods[] = { { "lookup", (PyCFunction)py_dsdb_dns_lookup, METH_VARARGS, "Get the DNS database entries for a DNS name"}, { "replace", (PyCFunction)py_dsdb_dns_replace, METH_VARARGS, "Replace the DNS database entries for a DNS name"}, + { "replace_by_dn", (PyCFunction)py_dsdb_dns_replace_by_dn, + METH_VARARGS, "Replace the DNS database entries for a LDB DN"}, { "extract", (PyCFunction)py_dsdb_dns_extract, METH_VARARGS, "Return the DNS database entry as a python structure from an Ldb.MessageElement of type dnsRecord"}, { NULL } From 3548047f5a23ebcf1c5dd88e691da3cb63e1fb15 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Wed, 14 Oct 2015 16:57:31 +1300 Subject: [PATCH 32/36] samba-tool domain demote: Remove all references to the demoted host, even in DNS We search the in-directory DNS records for entries that point to the name or IP that the dead DC was using, and remove them Signed-off-by: Andrew Bartlett --- python/samba/remove_dc.py | 81 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 78 insertions(+), 3 deletions(-) diff --git a/python/samba/remove_dc.py b/python/samba/remove_dc.py index af70f40..308f7d2 100644 --- a/python/samba/remove_dc.py +++ b/python/samba/remove_dc.py @@ -19,8 +19,8 @@ import ldb from ldb import LdbError from samba.ndr import ndr_unpack -from samba.dcerpc import misc - +from samba.dcerpc import misc, dnsp +from samba.dcerpc.dnsp import DNS_TYPE_NS, DNS_TYPE_A, DNS_TYPE_AAAA, DNS_TYPE_CNAME, DNS_TYPE_SRV, DNS_TYPE_PTR class demoteException(Exception): """Base element for demote errors""" @@ -82,14 +82,89 @@ def remove_dns_references(samdb, dnsHostName): if len(zones) == 0: return + dnsHostNameUpper = dnsHostName.upper() + try: - rec = samdb.dns_lookup(dnsHostName) + primary_recs = samdb.dns_lookup(dnsHostName) except RuntimeError as (enum, estr): if enum == 0x000025F2: #WERR_DNS_ERROR_NAME_DOES_NOT_EXIST return raise demoteException("lookup of %s failed: %s" % (dnsHostName, estr)) samdb.dns_replace(dnsHostName, []) + res = samdb.search("", + scope=ldb.SCOPE_BASE, attrs=["namingContexts"]) + assert len(res) == 1 + ncs = res[0]["namingContexts"] + + # Work out the set of names we will likely have an A record on by + # default. This is by default all the partitions of type + # domainDNS. By finding the canocial name of all the partitions, + # we find the likely candidates. We only remove the record if it + # maches the IP that was used by the dnsHostName. This avoids us + # needing to look a the dns_update_list file from in the demote + # script. + + a_names_to_remove_from = set([ ldb.Dn(samdb, dn).canonical_str().split('/', 1)[0] for dn in ncs]) + def a_rec_to_remove(dnsRecord): + if dnsRecord.wType == DNS_TYPE_A or dnsRecord.wType == DNS_TYPE_AAAA: + for rec in primary_recs: + if rec.wType == dnsRecord.wType and rec.data == dnsRecord.data: + return True + return False + + for a_name in a_names_to_remove_from: + try: + logger.debug("checking for DNS records to remove on %s" % a_name) + a_recs = samdb.dns_lookup(a_name) + except RuntimeError as (enum, estr): + if enum == 0x000025F2: #WERR_DNS_ERROR_NAME_DOES_NOT_EXIST + return + raise demoteException("lookup of %s failed: %s" % (a_name, estr)) + + orig_num_recs = len(a_recs) + a_recs = [ r for r in a_recs if not a_rec_to_remove(r) ] + + if len(a_recs) != orig_num_recs: + print "updating %s keeping %d values, removing %s values" % \ + (record.dn, len(a_recs), orig_num_recs - len(a_recs)) + samdb.dns_replace(a_name, a_recs) + + # Find all the CNAME, NS, PTR and SRV records that point at the + # name we are removing + + def to_remove(value): + dnsRecord = ndr_unpack(dnsp.DnssrvRpcRecord, value) + if dnsRecord.wType == DNS_TYPE_NS or dnsRecord.wType == DNS_TYPE_CNAME \ + or dnsRecord.wType == DNS_TYPE_PTR: + if dnsRecord.data.upper() == dnsHostNameUpper: + return True + elif dnsRecord.wType == DNS_TYPE_SRV: + if dnsRecord.data.nameTarget.upper() == dnsHostNameUpper: + return True + return False + + for zone in zones: + print "checking %s" % zone.dn + records = samdb.search(base=zone.dn, scope=ldb.SCOPE_SUBTREE, + expression="(&(objectClass=dnsNode)(!(dNSTombstoned=TRUE)))", + attrs=["dnsRecord"]) + for record in records: + try: + values = record["dnsRecord"] + except KeyError: + next + orig_num_values = len(values) + + # Remove references to dnsHostName in A, AAAA, NS, CNAME and SRV + values = [ ndr_unpack(dnsp.DnssrvRpcRecord, v) for v in values if not to_remove(v) ] + if len(values) != orig_num_values: + print "updating %s keeping %d values, removing %s values" % (record.dn, len(values), orig_num_values - len(values)) + + # This requires the values to be unpacked, so this + # has been done in the list comprehension above + samdb.dns_replace_by_dn(record.dn, values) + def offline_remove_server(samdb, server_dn, remove_computer_obj=False, remove_server_obj=False, From e37202418cb9e785140b1d35a50af18e7e0568a7 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Fri, 16 Oct 2015 13:00:20 +1300 Subject: [PATCH 33/36] samba-tool domain demote: Remove dns-SERVER object as well This object is not in standard AD, but Marc Muehlfeld correctly notes that Samba creates it for BIND9_DLZ Signed-off-by: Andrew Bartlett --- python/samba/remove_dc.py | 26 ++++++++++++++++++++------ python/samba/tests/blackbox/samba_tool_drs.py | 15 +++++++++++++++ 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/python/samba/remove_dc.py b/python/samba/remove_dc.py index 308f7d2..d9a13ec 100644 --- a/python/samba/remove_dc.py +++ b/python/samba/remove_dc.py @@ -169,7 +169,8 @@ def offline_remove_server(samdb, server_dn, remove_computer_obj=False, remove_server_obj=False, remove_sysvol_obj=False, - remove_dns_names=False): + remove_dns_names=False, + remove_dns_account=False): res = samdb.search("", scope=ldb.SCOPE_BASE, attrs=["dsServiceName"]) assert len(res) == 1 @@ -202,7 +203,8 @@ def offline_remove_server(samdb, server_dn, computer_msgs = samdb.search(base=computer_dn, expression="objectclass=computer", attrs=["msDS-KrbTgtLink", - "rIDSetReferences"], + "rIDSetReferences", + "cn"], scope=ldb.SCOPE_BASE) if "rIDSetReferences" in computer_msgs[0]: samdb.delete(computer_msgs[0]["rIDSetReferences"][0]) @@ -216,6 +218,14 @@ def offline_remove_server(samdb, server_dn, if "dnsHostName" in msgs[0]: dnsHostName = msgs[0]["dnsHostName"][0] + if remove_dns_account: + res = samdb.search(expression="(&(objectclass=user)(cn=dns-%s)(servicePrincipalName=DNS/%s))" % + (ldb.binary_encode(dc_name), dnsHostName), + attrs=[], scope=ldb.SCOPE_SUBTREE, + base=samdb.get_default_basedn()) + if len(res) == 1: + samdb.delete(res[0].dn) + if dnsHostName is not None and remove_dns_names: remove_dns_references(samdb, dnsHostName) @@ -228,7 +238,8 @@ def offline_remove_ntds_dc(samdb, ntds_dn, remove_connection_obj=False, seize_stale_fsmo=False, remove_sysvol_obj=False, - remove_dns_names=False): + remove_dns_names=False, + remove_dns_account=False): res = samdb.search("", scope=ldb.SCOPE_BASE, attrs=["dsServiceName"]) assert len(res) == 1 @@ -289,7 +300,8 @@ def offline_remove_ntds_dc(samdb, ntds_dn, remove_computer_obj=remove_computer_obj, remove_server_obj=remove_server_obj, remove_sysvol_obj=remove_sysvol_obj, - remove_dns_names=remove_dns_names) + remove_dns_names=remove_dns_names, + remove_dns_account=remove_dns_account) def remove_dc(samdb, dc_name): @@ -321,7 +333,8 @@ def remove_dc(samdb, dc_name): remove_computer_obj=True, remove_server_obj=True, remove_sysvol_obj=True, - remove_dns_names=True) + remove_dns_names=True, + remove_dns_account=True) samdb.transaction_commit() return @@ -334,7 +347,8 @@ def remove_dc(samdb, dc_name): remove_connection_obj=True, seize_stale_fsmo=True, remove_sysvol_obj=True, - remove_dns_names=True) + remove_dns_names=True, + remove_dns_account=True) samdb.transaction_commit() diff --git a/python/samba/tests/blackbox/samba_tool_drs.py b/python/samba/tests/blackbox/samba_tool_drs.py index 1d5f257..ca1d163c 100644 --- a/python/samba/tests/blackbox/samba_tool_drs.py +++ b/python/samba/tests/blackbox/samba_tool_drs.py @@ -167,6 +167,14 @@ def test_samba_tool_drs_clone_dc_secrets(self): server_dn = samdb.searchone("serverReferenceBL", "cn=%s,ou=domain controllers,%s" % (self.dc2, server_nc_name)) ntds_guid = samdb.searchone("objectGUID", "cn=ntds settings,%s" % server_dn) + res = samdb.search(base=str(server_nc_name), + expression="(&(objectclass=user)(cn=dns-%s))" % (self.dc2), + attrs=[], scope=ldb.SCOPE_SUBTREE) + if len(res) == 1: + dns_obj = res[0] + else: + dns_obj = None + def demote_self(): # While we have this cloned, try demoting the other server on the clone out = self.check_output("samba-tool domain demote --remove-other-dead-server=%s -H %s/private/sam.ldb" @@ -192,6 +200,13 @@ def check_ntds_guid(): samdb.searchone("CN", "" % ntds_guid) self.assertRaises(ldb.LdbError, check_ntds_guid) + if dns_obj is not None: + # Check some of the objects that should have been removed + def check_dns_account_obj(): + samdb.search(base=dns_obj.dn, scope=ldb.SCOPE_BASE, + attrs=[]) + self.assertRaises(ldb.LdbError, check_dns_account_obj) + shutil.rmtree(os.path.join(self.tempdir, "private")) shutil.rmtree(os.path.join(self.tempdir, "etc")) shutil.rmtree(os.path.join(self.tempdir, "msg.lock")) From 927ee1ea408be71287515440e57aa8df5233a9d2 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Fri, 16 Oct 2015 13:47:29 +1300 Subject: [PATCH 34/36] samba-tool domain demote: Add --verbose and --quiet options Signed-off-by: Andrew Bartlett --- python/samba/netcmd/domain.py | 15 +++++++-- python/samba/remove_dc.py | 50 ++++++++++++++++++---------- python/samba/tests/kcc/ldif_import_export.py | 5 +-- 3 files changed, 49 insertions(+), 21 deletions(-) diff --git a/python/samba/netcmd/domain.py b/python/samba/netcmd/domain.py index 7285ec1..4ccad79 100644 --- a/python/samba/netcmd/domain.py +++ b/python/samba/netcmd/domain.py @@ -653,6 +653,8 @@ class cmd_domain_demote(Command): Option("-H", "--URL", help="LDB URL for database or target server", type=str, metavar="URL", dest="H"), Option("--remove-other-dead-server", help="Dead DC to remove ALL references to (rather than this DC)", type=str), + Option("--quiet", help="Be quiet", action="store_true"), + Option("--verbose", help="Be verbose", action="store_true"), ] takes_optiongroups = { @@ -663,11 +665,20 @@ class cmd_domain_demote(Command): def run(self, sambaopts=None, credopts=None, versionopts=None, server=None, - remove_other_dead_server=None, H=None): + remove_other_dead_server=None, H=None, + verbose=False, quiet=False): lp = sambaopts.get_loadparm() creds = credopts.get_credentials(lp) net = Net(creds, lp, server=credopts.ipaddress) + logger = self.get_logger() + if verbose: + logger.setLevel(logging.DEBUG) + elif quiet: + logger.setLevel(logging.WARNING) + else: + logger.setLevel(logging.INFO) + if remove_other_dead_server is not None: if server is not None: samdb = SamDB(url="ldap://%s" % server, @@ -676,7 +687,7 @@ def run(self, sambaopts=None, credopts=None, else: samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp) try: - remove_dc.remove_dc(samdb, remove_other_dead_server) + remove_dc.remove_dc(samdb, logger, remove_other_dead_server) except remove_dc.demoteException as err: raise CommandError("Demote failed: %s" % err) return diff --git a/python/samba/remove_dc.py b/python/samba/remove_dc.py index d9a13ec..3594f6e 100644 --- a/python/samba/remove_dc.py +++ b/python/samba/remove_dc.py @@ -31,7 +31,7 @@ def __str__(self): return "demoteException: " + self.value -def remove_sysvol_references(samdb, dc_name): +def remove_sysvol_references(samdb, logger, dc_name): realm = samdb.domain_dns_name() for s in ("CN=Enterprise,CN=Microsoft System Volumes,CN=System", "CN=%s,CN=Microsoft System Volumes,CN=System" % realm): @@ -47,6 +47,7 @@ def remove_sysvol_references(samdb, dc_name): raise demoteException("Failed constructing DN %s by adding child CN=X"\ % (dn)) dn.set_component(0, "CN", dc_name) + logger.info("Removing Sysvol reference: %s" % dn) samdb.delete(dn) except ldb.LdbError as (enum, estr): if enum == ldb.ERR_NO_SUCH_OBJECT: @@ -65,6 +66,7 @@ def remove_sysvol_references(samdb, dc_name): raise demoteException("Failed constructing DN %s by adding child %s"\ % (dn, rdn)) dn.set_component(0, "CN", dc_name) + logger.info("Removing Sysvol reference: %s" % dn) samdb.delete(dn) except ldb.LdbError as (enum, estr): if enum == ldb.ERR_NO_SUCH_OBJECT: @@ -72,7 +74,7 @@ def remove_sysvol_references(samdb, dc_name): else: raise -def remove_dns_references(samdb, dnsHostName): +def remove_dns_references(samdb, logger, dnsHostName): # Check we are using in-database DNS zones = samdb.search(base="", scope=ldb.SCOPE_SUBTREE, @@ -126,8 +128,8 @@ def a_rec_to_remove(dnsRecord): a_recs = [ r for r in a_recs if not a_rec_to_remove(r) ] if len(a_recs) != orig_num_recs: - print "updating %s keeping %d values, removing %s values" % \ - (record.dn, len(a_recs), orig_num_recs - len(a_recs)) + logger.info("updating %s keeping %d values, removing %s values" % \ + (record.dn, len(a_recs), orig_num_recs - len(a_recs))) samdb.dns_replace(a_name, a_recs) # Find all the CNAME, NS, PTR and SRV records that point at the @@ -145,7 +147,7 @@ def to_remove(value): return False for zone in zones: - print "checking %s" % zone.dn + logger.debug("checking %s" % zone.dn) records = samdb.search(base=zone.dn, scope=ldb.SCOPE_SUBTREE, expression="(&(objectClass=dnsNode)(!(dNSTombstoned=TRUE)))", attrs=["dnsRecord"]) @@ -159,13 +161,14 @@ def to_remove(value): # Remove references to dnsHostName in A, AAAA, NS, CNAME and SRV values = [ ndr_unpack(dnsp.DnssrvRpcRecord, v) for v in values if not to_remove(v) ] if len(values) != orig_num_values: - print "updating %s keeping %d values, removing %s values" % (record.dn, len(values), orig_num_values - len(values)) + logger.info("updating %s keeping %d values, removing %s values" % (record.dn, len(values), orig_num_values - len(values))) # This requires the values to be unpacked, so this # has been done in the list comprehension above samdb.dns_replace_by_dn(record.dn, values) -def offline_remove_server(samdb, server_dn, +def offline_remove_server(samdb, logger, + server_dn, remove_computer_obj=False, remove_server_obj=False, remove_sysvol_obj=False, @@ -207,12 +210,17 @@ def offline_remove_server(samdb, server_dn, "cn"], scope=ldb.SCOPE_BASE) if "rIDSetReferences" in computer_msgs[0]: - samdb.delete(computer_msgs[0]["rIDSetReferences"][0]) + rid_set_dn = str(computer_msgs[0]["rIDSetReferences"][0]) + logger.info("Removing RID Set: %s" % rid_set_dn) + samdb.delete(rid_set_dn) if "msDS-KrbTgtLink" in computer_msgs[0]: - samdb.delete(computer_msgs[0]["msDS-KrbTgtLink"][0]) + krbtgt_link_dn = str(computer_msgs[0]["msDS-KrbTgtLink"][0]) + logger.info("Removing RODC KDC account: %s" % krbtgt_link_dn) + samdb.delete(krbtgt_link_dn) if remove_computer_obj: # Delete the computer tree + logger.info("Removing computer account: %s (and any child objects)" % computer_dn) samdb.delete(computer_dn, ["tree_delete:0"]) if "dnsHostName" in msgs[0]: @@ -224,15 +232,18 @@ def offline_remove_server(samdb, server_dn, attrs=[], scope=ldb.SCOPE_SUBTREE, base=samdb.get_default_basedn()) if len(res) == 1: + logger.info("Removing Samba-specific DNS service account: %s" % res[0].dn) samdb.delete(res[0].dn) if dnsHostName is not None and remove_dns_names: - remove_dns_references(samdb, dnsHostName) + remove_dns_references(samdb, logger, dnsHostName) if remove_sysvol_obj: - remove_sysvol_references(samdb, dc_name) + remove_sysvol_references(samdb, logger, dc_name) -def offline_remove_ntds_dc(samdb, ntds_dn, +def offline_remove_ntds_dc(samdb, + logger, + ntds_dn, remove_computer_obj=False, remove_server_obj=False, remove_connection_obj=False, @@ -275,6 +286,7 @@ def offline_remove_ntds_dc(samdb, ntds_dn, stale_connections = samdb.search(base=samdb.get_config_basedn(), expression="(&(objectclass=nTDSConnection)(fromServer=))" % ntds_guid) for conn in stale_connections: + logger.info("Removing nTDSConnection: %s" % conn.dn) samdb.delete(conn.dn) if seize_stale_fsmo: @@ -288,15 +300,17 @@ def offline_remove_ntds_dc(samdb, ntds_dn, m = ldb.Message() m.dn = role.dn m['value'] = ldb.MessageElement(val, ldb.FLAG_MOD_REPLACE, 'fsmoRoleOwner') + logger.warning("Seizing FSMO role on: %s (now owned by %s)" % (role.dn, my_serviceName)) samdb.modify(m) # Remove the NTDS setting tree try: + logger.info("Removing nTDSDSA: %s (and any children)" % ntds_dn) samdb.delete(ntds_dn, ["tree_delete:0"]) except LdbError as (enum, estr): raise demoteException("Failed to remove the DCs NTDS DSA object: %s" % estr) - offline_remove_server(samdb, server_dn, + offline_remove_server(samdb, logger, server_dn, remove_computer_obj=remove_computer_obj, remove_server_obj=remove_server_obj, remove_sysvol_obj=remove_sysvol_obj, @@ -304,7 +318,7 @@ def offline_remove_ntds_dc(samdb, ntds_dn, remove_dns_account=remove_dns_account) -def remove_dc(samdb, dc_name): +def remove_dc(samdb, logger, dc_name): # TODO: Check if this is the last server (covered a mostly by # refusing to remove our own name) @@ -329,7 +343,8 @@ def remove_dc(samdb, dc_name): expression="(objectClass=ntdsdsa)") except LdbError as (enum, estr): if enum == ldb.ERR_NO_SUCH_OBJECT: - offline_remove_server(samdb, msgs[0].dn, + offline_remove_server(samdb, logger, + msgs[0].dn, remove_computer_obj=True, remove_server_obj=True, remove_sysvol_obj=True, @@ -341,7 +356,8 @@ def remove_dc(samdb, dc_name): else: pass - offline_remove_ntds_dc(samdb, msgs[0].dn, + offline_remove_ntds_dc(samdb, logger, + msgs[0].dn, remove_computer_obj=True, remove_server_obj=True, remove_connection_obj=True, @@ -358,6 +374,6 @@ def offline_remove_dc_RemoveDsServer(samdb, ntds_dn): samdb.start_transaction() - offline_remove_ntds_dc(samdb, ntds_dn) + offline_remove_ntds_dc(samdb, ntds_dn, None) samdb.commit_transaction() diff --git a/python/samba/tests/kcc/ldif_import_export.py b/python/samba/tests/kcc/ldif_import_export.py index a9e11cd..862ec50 100644 --- a/python/samba/tests/kcc/ldif_import_export.py +++ b/python/samba/tests/kcc/ldif_import_export.py @@ -25,7 +25,7 @@ import shutil import sys import subprocess - +import logging import samba.tests from samba.kcc import ldif_import_export, KCC from samba import ldb @@ -207,7 +207,8 @@ def test_verify_with_dc_removal(self): if dsa == forced_dsa: continue rdn = ldb.Dn(my_kcc.samdb, dsa).get_rdn_value() - remove_dc.remove_dc(my_kcc.samdb, rdn) + logger = logging.getLogger('kcc.ldif_import_export') + remove_dc.remove_dc(my_kcc.samdb, logger, rdn) my_kcc.run(None, self.lp, self.creds, attempt_live_connections=False) From 85065f61aff538da9e796f57adf1844ff2b21e4f Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Tue, 20 Oct 2015 13:48:07 +1300 Subject: [PATCH 35/36] samba-tool domain demote: Add support for removing by NTDS GUID This would help remove a DC that is a conflict record, for example Signed-off-by: Andrew Bartlett --- python/samba/netcmd/domain.py | 2 +- python/samba/remove_dc.py | 88 +++++++++++++++++---------- python/samba/tests/blackbox/samba_tool_drs.py | 37 +++++++++++ 3 files changed, 93 insertions(+), 34 deletions(-) diff --git a/python/samba/netcmd/domain.py b/python/samba/netcmd/domain.py index 4ccad79..5679c89 100644 --- a/python/samba/netcmd/domain.py +++ b/python/samba/netcmd/domain.py @@ -652,7 +652,7 @@ class cmd_domain_demote(Command): Option("--server", help="writable DC to write demotion changes on", type=str), Option("-H", "--URL", help="LDB URL for database or target server", type=str, metavar="URL", dest="H"), - Option("--remove-other-dead-server", help="Dead DC to remove ALL references to (rather than this DC)", type=str), + Option("--remove-other-dead-server", help="Dead DC (name or NTDS GUID) to remove ALL references to (rather than this DC)", type=str), Option("--quiet", help="Be quiet", action="store_true"), Option("--verbose", help="Be verbose", action="store_true"), ] diff --git a/python/samba/remove_dc.py b/python/samba/remove_dc.py index 3594f6e..a7768c5 100644 --- a/python/samba/remove_dc.py +++ b/python/samba/remove_dc.py @@ -16,6 +16,7 @@ # along with this program. If not, see . # +import uuid import ldb from ldb import LdbError from samba.ndr import ndr_unpack @@ -325,46 +326,67 @@ def remove_dc(samdb, logger, dc_name): samdb.transaction_start() - msgs = samdb.search(base=samdb.get_config_basedn(), - attrs=["serverReference"], - expression="(&(objectClass=server)(cn=%s))" - % ldb.binary_encode(dc_name)) - if (len(msgs) == 0): - raise demoteException("%s is not an AD DC in %s" - % (dc_name, samdb.domain_dns_name())) - server_dn = msgs[0].dn + server_dn = None - ntds_dn = ldb.Dn(samdb, "CN=NTDS Settings") - ntds_dn.add_base(msgs[0].dn) + # Allow the name to be a the nTDS-DSA GUID + try: + ntds_guid = uuid.UUID(hex=dc_name) + ntds_dn = "" % ntds_guid + except ValueError: + try: + server_msgs = samdb.search(base=samdb.get_config_basedn(), + attrs=[], + expression="(&(objectClass=server)(cn=%s))" + % ldb.binary_encode(dc_name)) + except LdbError as (enum, estr): + raise demoteException("Failure checking if %s is an server object in %s: " + % (dc_name, samdb.domain_dns_name()), estr) + + if (len(server_msgs) == 0): + raise demoteException("%s is not an AD DC in %s" + % (dc_name, samdb.domain_dns_name())) + server_dn = server_msgs[0].dn + + ntds_dn = ldb.Dn(samdb, "CN=NTDS Settings") + ntds_dn.add_base(server_dn) + pass # Confirm this is really an ntdsDSA object try: - msgs = samdb.search(base=ntds_dn, attrs=[], scope=ldb.SCOPE_BASE, - expression="(objectClass=ntdsdsa)") + ntds_msgs = samdb.search(base=ntds_dn, attrs=[], scope=ldb.SCOPE_BASE, + expression="(objectClass=ntdsdsa)") except LdbError as (enum, estr): if enum == ldb.ERR_NO_SUCH_OBJECT: - offline_remove_server(samdb, logger, - msgs[0].dn, - remove_computer_obj=True, - remove_server_obj=True, - remove_sysvol_obj=True, - remove_dns_names=True, - remove_dns_account=True) - - samdb.transaction_commit() - return - else: + ntds_msgs = [] pass - - offline_remove_ntds_dc(samdb, logger, - msgs[0].dn, - remove_computer_obj=True, - remove_server_obj=True, - remove_connection_obj=True, - seize_stale_fsmo=True, - remove_sysvol_obj=True, - remove_dns_names=True, - remove_dns_account=True) + else: + raise demoteException("Failure checking if %s is an NTDS DSA in %s: " + % (ntds_dn, samdb.domain_dns_name()), estr) + + # If the NTDS Settings child DN wasn't found or wasnt an ntdsDSA + # object, just remove the server object located above + if (len(ntds_msgs) == 0): + if server_dn is None: + raise demoteException("%s is not an AD DC in %s" + % (dc_name, samdb.domain_dns_name())) + + offline_remove_server(samdb, logger, + server_dn, + remove_computer_obj=True, + remove_server_obj=True, + remove_sysvol_obj=True, + remove_dns_names=True, + remove_dns_account=True) + else: + offline_remove_ntds_dc(samdb, logger, + ntds_msgs[0].dn, + remove_computer_obj=True, + remove_server_obj=True, + remove_connection_obj=True, + seize_stale_fsmo=True, + remove_sysvol_obj=True, + remove_dns_names=True, + remove_dns_account=True) samdb.transaction_commit() diff --git a/python/samba/tests/blackbox/samba_tool_drs.py b/python/samba/tests/blackbox/samba_tool_drs.py index ca1d163c..4d20852 100644 --- a/python/samba/tests/blackbox/samba_tool_drs.py +++ b/python/samba/tests/blackbox/samba_tool_drs.py @@ -130,6 +130,43 @@ def test_samba_tool_drs_clone_dc(self): def get_krbtgt_pw(): krbtgt_pw = samdb.searchone("unicodePwd", "cn=krbtgt,CN=users,%s" % nc_name) self.assertRaises(KeyError, get_krbtgt_pw) + + server_dn = samdb.searchone("serverReferenceBL", "cn=%s,ou=domain controllers,%s" % (self.dc2, server_nc_name)) + ntds_guid = samdb.searchone("objectGUID", "cn=ntds settings,%s" % server_dn) + + res = samdb.search(base=str(server_nc_name), + expression="(&(objectclass=user)(cn=dns-%s))" % (self.dc2), + attrs=[], scope=ldb.SCOPE_SUBTREE) + if len(res) == 1: + dns_obj = res[0] + else: + dns_obj = None + + # While we have this cloned, try demoting the other server on the clone, by GUID + out = self.check_output("samba-tool domain demote --remove-other-dead-server=%s -H %s/private/sam.ldb" + % (ntds_guid, + self.tempdir)) + + # Check some of the objects that should have been removed + def check_machine_obj(): + samdb.searchone("CN", "cn=%s,ou=domain controllers,%s" % (self.dc2, server_nc_name)) + self.assertRaises(ldb.LdbError, check_machine_obj) + + def check_server_obj(): + samdb.searchone("CN", server_dn) + self.assertRaises(ldb.LdbError, check_server_obj) + + def check_ntds_guid(): + samdb.searchone("CN", "" % ntds_guid) + self.assertRaises(ldb.LdbError, check_ntds_guid) + + if dns_obj is not None: + # Check some of the objects that should have been removed + def check_dns_account_obj(): + samdb.search(base=dns_obj.dn, scope=ldb.SCOPE_BASE, + attrs=[]) + self.assertRaises(ldb.LdbError, check_dns_account_obj) + shutil.rmtree(os.path.join(self.tempdir, "private")) shutil.rmtree(os.path.join(self.tempdir, "etc")) shutil.rmtree(os.path.join(self.tempdir, "msg.lock")) From 8eb069e667551cb1a6352f9b4a5a569abc977fcf Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Thu, 22 Oct 2015 12:01:48 +1300 Subject: [PATCH 36/36] Add samba4.smb2.create.mkdir-dup(ad_dc_ntvfs) as flapping Signed-off-by: Andrew Bartlett --- selftest/flapping | 1 + 1 file changed, 1 insertion(+) diff --git a/selftest/flapping b/selftest/flapping index 8e13dc3..6b7af8e 100644 --- a/selftest/flapping +++ b/selftest/flapping @@ -29,3 +29,4 @@ ^samba4.drs.delete_object.python # flakey on sn-devel ^samba4.blackbox.samba_tool_demote # flakey on sn-devel ^samba4.blackbox.dbcheck # flakey on sn-devel +^samba4.smb2.create.mkdir-dup\(ad_dc_ntvfs\) # This test involves a race, not always protected against in the NTVFS file server