From e0bb2b2448a7dfc1d455041f2a36c9956d0b610d Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Tue, 17 Mar 2015 16:02:52 +1300 Subject: [PATCH 01/40] dsdb: Add functional levels for 2012 and 2012R2 Signed-off-by: Andrew Bartlett --- libds/common/flags.h | 2 ++ source4/dsdb/pydsdb.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/libds/common/flags.h b/libds/common/flags.h index f821e17..de3d675 100644 --- a/libds/common/flags.h +++ b/libds/common/flags.h @@ -188,6 +188,8 @@ #define DS_DOMAIN_FUNCTION_2003 2 #define DS_DOMAIN_FUNCTION_2008 3 #define DS_DOMAIN_FUNCTION_2008_R2 4 +#define DS_DOMAIN_FUNCTION_2012 5 +#define DS_DOMAIN_FUNCTION_2012_R2 6 /* sa->systemFlags on attributes */ #define DS_FLAG_ATTR_NOT_REPLICATED 0x00000001 diff --git a/source4/dsdb/pydsdb.c b/source4/dsdb/pydsdb.c index 9a3b509..8836d85 100644 --- a/source4/dsdb/pydsdb.c +++ b/source4/dsdb/pydsdb.c @@ -1180,6 +1180,8 @@ void initdsdb(void) ADD_DSDB_FLAG(DS_DOMAIN_FUNCTION_2003); ADD_DSDB_FLAG(DS_DOMAIN_FUNCTION_2008); ADD_DSDB_FLAG(DS_DOMAIN_FUNCTION_2008_R2); + ADD_DSDB_FLAG(DS_DOMAIN_FUNCTION_2012); + ADD_DSDB_FLAG(DS_DOMAIN_FUNCTION_2012_R2); /* nc replica flags */ ADD_DSDB_FLAG(INSTANCE_TYPE_IS_NC_HEAD); From 0f325c2bbb2d9c897efa46051535f8ff294a3ffc Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Tue, 17 Mar 2015 16:05:37 +1300 Subject: [PATCH 02/40] provision: Allow more OS levels in sambadns While we do not support these yet, they make no difference to DNS, so permit up to 2012R2 Signed-off-by: Andrew Bartlett --- python/samba/provision/sambadns.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/python/samba/provision/sambadns.py b/python/samba/provision/sambadns.py index b563932..f3cdb32 100644 --- a/python/samba/provision/sambadns.py +++ b/python/samba/provision/sambadns.py @@ -35,7 +35,8 @@ from samba.dsdb import ( DS_DOMAIN_FUNCTION_2000, DS_DOMAIN_FUNCTION_2003, - DS_DOMAIN_FUNCTION_2008_R2 + DS_DOMAIN_FUNCTION_2008_R2, + DS_DOMAIN_FUNCTION_2012_R2 ) from samba.descriptor import ( get_domain_descriptor, @@ -967,7 +968,7 @@ def is_valid_dns_backend(dns_backend): def is_valid_os_level(os_level): - return DS_DOMAIN_FUNCTION_2000 <= os_level <= DS_DOMAIN_FUNCTION_2008_R2 + return DS_DOMAIN_FUNCTION_2000 <= os_level <= DS_DOMAIN_FUNCTION_2012_R2 def create_dns_legacy(samdb, domainsid, forestdn, dnsadmins_sid): From 280e6f34c9b1617ed2c1ef3ed5bfdb610ac6d964 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Mon, 17 Aug 2015 15:33:31 +1200 Subject: [PATCH 03/40] 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 --- 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 ba1a5d47a71d4a930553d3253673127e86a23fcd Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Wed, 19 Aug 2015 13:26:41 +1200 Subject: [PATCH 04/40] 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 69195af..01f2487 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 3e37c0360bdc40b0b9d1df754f240aab7eebdb55 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Wed, 19 Aug 2015 13:29:35 +1200 Subject: [PATCH 05/40] 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 202699bde6c61cb61dccdea464492159ecb1a572 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Wed, 19 Aug 2015 13:30:55 +1200 Subject: [PATCH 06/40] 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 01f2487..d08dc9c 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 a019e3a20135b5ba04df5284eb0499df56eb5907 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Tue, 25 Aug 2015 15:51:19 +1200 Subject: [PATCH 07/40] 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 d08dc9c..97c6d1a 100644 --- a/source4/libnet/libnet_vampire.c +++ b/source4/libnet/libnet_vampire.c @@ -757,285 +757,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 b222cfc9895ea429fac2b4c9cff5a433ce0701ce Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Mon, 14 Sep 2015 12:12:31 +1200 Subject: [PATCH 08/40] 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 e302d2dc8bfc007b3a100a46f64f3e783e886d9c Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Mon, 14 Sep 2015 13:48:04 +1200 Subject: [PATCH 09/40] 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 ca2dc47..a1ec12c 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 746e97c8409a5285722744cd45a2a3b7f0dc7f41 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Mon, 14 Sep 2015 13:47:31 +1200 Subject: [PATCH 10/40] 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 9c19141ccf41a34a133a0930680a22e90035abc3 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Mon, 14 Sep 2015 15:06:42 +1200 Subject: [PATCH 11/40] 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 | 804 ++++++++++++++++++++++++++++++++ 1 file changed, 804 insertions(+) diff --git a/testdata/ldif-utils-test-multisite.ldif b/testdata/ldif-utils-test-multisite.ldif index 175b9da..75520ac 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,792 @@ 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 +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=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 +whenChanged: 20150227014849.0Z + +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 7fa944cc0516f73a857a32f5e8e79bf6020076d2 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Mon, 14 Sep 2015 15:38:21 +1200 Subject: [PATCH 12/40] 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 a1ec12c..e13db58 100644 --- a/python/samba/kcc/ldif_import_export.py +++ b/python/samba/kcc/ldif_import_export.py @@ -366,6 +366,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: @@ -376,6 +377,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 15a9937d5f733df5d68dfee09215cda05a67d180 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Mon, 14 Sep 2015 15:38:50 +1200 Subject: [PATCH 13/40] testdata: Include more of the multi-site network in the test fixture This allows us to test removing an RODC from a realistic network, including the KDC Signed-off-by: Andrew Bartlett --- testdata/ldif-utils-test-multisite.ldif | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/testdata/ldif-utils-test-multisite.ldif b/testdata/ldif-utils-test-multisite.ldif index 75520ac..fd2e1fa 100644 --- a/testdata/ldif-utils-test-multisite.ldif +++ b/testdata/ldif-utils-test-multisite.ldif @@ -1405,6 +1405,7 @@ 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 @@ -1428,6 +1429,16 @@ 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 @@ -1574,8 +1585,19 @@ 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 From 5974503d14aa1875773abff9c4ad6a31faf679d8 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Tue, 22 Sep 2015 12:10:00 +1200 Subject: [PATCH 14/40] 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 6c0c67c0e294cf8bc315d08d66f97b538541d39a Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Tue, 22 Sep 2015 12:11:04 +1200 Subject: [PATCH 15/40] 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 5b95476e90581790d135d672498eaa29cb591924 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Tue, 22 Sep 2015 15:25:30 +1200 Subject: [PATCH 16/40] 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/dns_server/dlz_bind9.c | 16 +++--- source4/dsdb/pydsdb.c | 113 ++++++++++++++++++++++------------------- 3 files changed, 71 insertions(+), 62 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/dns_server/dlz_bind9.c b/source4/dns_server/dlz_bind9.c index 7a76fe5..068ec44 100644 --- a/source4/dns_server/dlz_bind9.c +++ b/source4/dns_server/dlz_bind9.c @@ -972,6 +972,14 @@ _PUBLIC_ isc_result_t dlz_allnodes(const char *zone, void *dbdata, continue; } + werr = dns_common_extract(el, el_ctx, &recs, &num_recs); + if (!W_ERROR_IS_OK(werr)) { + state->log(ISC_LOG_ERROR, "samba_dlz: failed to parse dnsRecord for %s, %s", + ldb_dn_get_linearized(dn), win_errstr(werr)); + talloc_free(el_ctx); + continue; + } + v = ldb_dn_get_rdn_val(res->msgs[i]->dn); if (v == NULL) { state->log(ISC_LOG_INFO, "failed to find RDN for %s", @@ -997,14 +1005,6 @@ _PUBLIC_ isc_result_t dlz_allnodes(const char *zone, void *dbdata, return ISC_R_NOMEMORY; } - werr = dns_common_extract(el, el_ctx, &recs, &num_recs); - if (!W_ERROR_IS_OK(werr)) { - state->log(ISC_LOG_ERROR, "samba_dlz: failed to parse dnsRecord for %s, %s", - ldb_dn_get_linearized(dn), win_errstr(werr)); - talloc_free(el_ctx); - continue; - } - for (j=0; j < num_recs; j++) { isc_result_t result; 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 a72af106a7cda8a7a8a75cc261db7e000bb32121 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Tue, 22 Sep 2015 15:32:57 +1200 Subject: [PATCH 17/40] 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 1a9ca43774b6ac702217e838d9d38304d004b2b5 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Mon, 14 Sep 2015 15:56:52 +1200 Subject: [PATCH 18/40] 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 f0710f2..f10eabe 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 8e72e3a6d5d5c890393cf29edba2507566d04c31 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Tue, 22 Sep 2015 15:34:10 +1200 Subject: [PATCH 19/40] 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 1d61b1c918e0da664b050dba369a46b481c6cfdf Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Tue, 22 Sep 2015 15:39:19 +1200 Subject: [PATCH 20/40] 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 9ca062c4590c4607ebf599872ac61f8f457d5026 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Tue, 22 Sep 2015 15:40:00 +1200 Subject: [PATCH 21/40] 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 4259e8cc010e6afa5316ece0943b506b0a7a23c4 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Tue, 22 Sep 2015 15:51:33 +1200 Subject: [PATCH 22/40] 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 255334e7b4a0d9348ef664ab569b2ff49eec4f61 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Thu, 24 Sep 2015 14:07:51 +1200 Subject: [PATCH 23/40] samba-tool demote: Rework to allow cleanup of partial demotion, catch more errors Signed-off-by: Andrew Bartlett --- python/samba/remove_dc.py | 154 +++++++++++++++++++++++++++++++--------------- 1 file changed, 103 insertions(+), 51 deletions(-) diff --git a/python/samba/remove_dc.py b/python/samba/remove_dc.py index 5bce244..b19fc76 100644 --- a/python/samba/remove_dc.py +++ b/python/samba/remove_dc.py @@ -20,6 +20,15 @@ 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 +59,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 +88,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 +116,69 @@ 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 + samdb.delete(ntds_dn, ["tree_delete:0"]) + + 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 +189,38 @@ 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() + 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 +230,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 5383e0c4381e0c7e810bb2be7d64a4ca5c7140db Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Thu, 24 Sep 2015 14:08:37 +1200 Subject: [PATCH 24/40] 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 | 28 +++++++++++++++---------- 1 file changed, 17 insertions(+), 11 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..5a83c68 100644 --- a/source4/dsdb/samdb/ldb_modules/repl_meta_data.c +++ b/source4/dsdb/samdb/ldb_modules/repl_meta_data.c @@ -3159,15 +3159,15 @@ 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_asprintf_errstring(ldb, __location__ ": Failed to add isDeleted string to the msg"); ldb_module_oom(module); talloc_free(tmp_ctx); return ret; @@ -3182,8 +3182,8 @@ 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 +3191,9 @@ 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; } @@ -3238,7 +3238,8 @@ static int replmd_delete_internals(struct ldb_module *module, struct ldb_request ret = ldb_msg_add_steal_string(msg, "lastKnownParent", ldb_dn_get_extended_linearized(tmp_ctx, parent_res->msgs[0]->dn, 1)); if (ret != LDB_SUCCESS) { - DEBUG(0,(__location__ ": Failed to add lastKnownParent string to the msg\n")); + ldb_asprintf_errstring(ldb, __location__ ": Failed to add lastKnownParent string when deleting %s", + ldb_dn_get_linearized(old_dn)); ldb_module_oom(module); talloc_free(tmp_ctx); return ret; @@ -3248,8 +3249,8 @@ 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 +3317,11 @@ 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) { + ldb_asprintf_errstring(ldb, + __location__ + ": Failed to remove backlink of %s when deleting %s", + el->name, + ldb_dn_get_linearized(old_dn)); talloc_free(tmp_ctx); return LDB_ERR_OPERATIONS_ERROR; } From 35d68db919bd61cc7b26e134eb568d3071b12d66 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Mon, 12 Oct 2015 15:51:37 +1300 Subject: [PATCH 25/40] 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. Signed-off-by: Andrew Bartlett --- source4/dsdb/samdb/ldb_modules/repl_meta_data.c | 92 +++++++++++++++++-------- 1 file changed, 62 insertions(+), 30 deletions(-) diff --git a/source4/dsdb/samdb/ldb_modules/repl_meta_data.c b/source4/dsdb/samdb/ldb_modules/repl_meta_data.c index 5a83c68..b7bf6d8 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,17 @@ 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; 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 +2930,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 +2945,53 @@ 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_val = data_blob_string_const(dsdb_dn_get_linearized(tmp_ctx, + p->dsdb_dn)); el2->values = &dn_val; el2->num_values = 1; @@ -3149,10 +3187,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, @@ -3306,7 +3344,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 != 0 && (sa->linkID & 1)) { /* we have a backlink in this object that needs to be removed. We're not @@ -3315,23 +3353,17 @@ 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); - if (ret != LDB_SUCCESS) { - ldb_asprintf_errstring(ldb, - __location__ - ": Failed to remove backlink of %s when deleting %s", - el->name, - ldb_dn_get_linearized(old_dn)); - talloc_free(tmp_ctx); - return LDB_ERR_OPERATIONS_ERROR; + ret = replmd_delete_remove_link(module, schema, + old_dn, &guid, + el, sa, req); + if (ret == LDB_SUCCESS) { + /* now we continue, which means we + won't remove this backlink + directly + */ + continue; } - /* now we continue, which means we - won't remove this backlink - directly - */ - continue; - } - if (!sa->linkID) { + } else if (sa->linkID == 0) { if (ldb_attr_in_list(preserved_attrs, el->name)) { continue; } From 80f1b8ea7456555440600ef6c7d26f0ac756cc52 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Mon, 12 Oct 2015 17:50:27 +1300 Subject: [PATCH 26/40] 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 a74424625053142457c3d78a0ed07918ff308a0a Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Tue, 13 Oct 2015 15:23:55 +1300 Subject: [PATCH 27/40] 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 58110009fc5f3e7b5841131a3c420427625127d2 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Tue, 13 Oct 2015 15:26:20 +1300 Subject: [PATCH 28/40] 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 b19fc76..5efb4f7 100644 --- a/python/samba/remove_dc.py +++ b/python/samba/remove_dc.py @@ -126,9 +126,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) @@ -182,7 +185,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 6273210190d2e1ea19b6b6312eb647ad583cc612 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Tue, 13 Oct 2015 16:41:44 +1300 Subject: [PATCH 29/40] 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 a8de968154bd6cb5aa359df7f52c90de18975be7 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Wed, 14 Oct 2015 11:58:22 +1300 Subject: [PATCH 30/40] 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 47b4293dccbb0456a0c2632112f6a1e8e16a795c Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Wed, 14 Oct 2015 11:59:26 +1300 Subject: [PATCH 31/40] 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 c48f66127818734a46889bbc4c3287fcd757bf14 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Wed, 14 Oct 2015 13:49:01 +1300 Subject: [PATCH 32/40] 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 305ec5250f39c8f63305b9cac2dc2d58d36a38b8 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Wed, 14 Oct 2015 13:52:45 +1300 Subject: [PATCH 33/40] 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 5efb4f7..df8f592 100644 --- a/python/samba/remove_dc.py +++ b/python/samba/remove_dc.py @@ -30,16 +30,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): @@ -76,7 +106,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]) @@ -114,7 +144,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 313cc2544bb5a00ddc7304c4c431000ca3540399 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Wed, 14 Oct 2015 16:56:41 +1300 Subject: [PATCH 34/40] 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 4dec2dce07e70c75d902292056be6207d19ba861 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Wed, 14 Oct 2015 16:57:31 +1300 Subject: [PATCH 35/40] 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 | 45 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 42 insertions(+), 3 deletions(-) diff --git a/python/samba/remove_dc.py b/python/samba/remove_dc.py index df8f592..944bf96 100644 --- a/python/samba/remove_dc.py +++ b/python/samba/remove_dc.py @@ -18,8 +18,8 @@ import ldb 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 class demoteException(Exception): """Base element for demote errors""" @@ -81,14 +81,53 @@ 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, []) + 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 + elif dnsRecord.wType == DNS_TYPE_A or dnsRecord.wType == DNS_TYPE_AAAA: + for rec in primary_recs: + if rec.wType == dnsRecord.wType: + if dnsRecord.data == rec.data: + 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 3a889d95132e78b8124dfc25c6f8a2d65fefd434 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Thu, 15 Oct 2015 16:23:08 +1300 Subject: [PATCH 36/40] samba-tool domain demote: Trap exceptions in the offline demote case This prints a nice error message rather than just a backtrace Signed-off-by: Andrew Bartlett --- python/samba/netcmd/domain.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/python/samba/netcmd/domain.py b/python/samba/netcmd/domain.py index f10eabe..9d3132c 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") From 56805fc88f8c275c181b9db1f1402144ed9d509d Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Fri, 16 Oct 2015 11:27:26 +1300 Subject: [PATCH 37/40] samba-tool domain demote: Be more careful when removing A and AAAA records We only wish to remove A and AAAA records that were created by samba_dnsupdate, not any other A record pointing to the host. Signed-off-by: Andrew Bartlett --- python/samba/remove_dc.py | 46 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 41 insertions(+), 5 deletions(-) diff --git a/python/samba/remove_dc.py b/python/samba/remove_dc.py index 944bf96..ffb535c 100644 --- a/python/samba/remove_dc.py +++ b/python/samba/remove_dc.py @@ -91,6 +91,47 @@ def remove_dns_references(samdb, dnsHostName): 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 \ @@ -100,11 +141,6 @@ def to_remove(value): elif dnsRecord.wType == DNS_TYPE_SRV: if dnsRecord.data.nameTarget.upper() == dnsHostNameUpper: return True - elif dnsRecord.wType == DNS_TYPE_A or dnsRecord.wType == DNS_TYPE_AAAA: - for rec in primary_recs: - if rec.wType == dnsRecord.wType: - if dnsRecord.data == rec.data: - return True return False for zone in zones: From a485867d24d89825e8db46fa3ecddcd4e43df634 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Fri, 16 Oct 2015 12:25:24 +1300 Subject: [PATCH 38/40] samba-tool domain demote: Catch exceptions when deleting NTDS DSA objects This means we give a nice message back to the user Signed-off-by: Andrew Bartlett --- python/samba/remove_dc.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/python/samba/remove_dc.py b/python/samba/remove_dc.py index ffb535c..94634e1 100644 --- a/python/samba/remove_dc.py +++ b/python/samba/remove_dc.py @@ -17,6 +17,7 @@ # import ldb +from ldb import LdbError from samba.ndr import ndr_unpack 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 @@ -279,7 +280,10 @@ def offline_remove_ntds_dc(samdb, ntds_dn, samdb.modify(m) # Remove the NTDS setting tree - samdb.delete(ntds_dn, ["tree_delete:0"]) + 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, From 5effe777c92c0d9720f97517eb450b6c716bace0 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Fri, 16 Oct 2015 13:00:20 +1300 Subject: [PATCH 39/40] samba-tool domain demote: Remove dns-SERVER object as well 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 94634e1..9b0b0f3 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() else: @@ -333,7 +346,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 37a212b56bfa23cb24785350489534dcfc486073 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Fri, 16 Oct 2015 13:47:29 +1300 Subject: [PATCH 40/40] 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 9d3132c..fa5a298 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 9b0b0f3..e02a500 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, @@ -340,7 +355,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, @@ -357,6 +373,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)