1
0
mirror of https://github.com/samba-team/samba.git synced 2024-12-22 13:34:15 +03:00

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 <abartlet@samba.org>
Reviewed-by: Garming Sam <garming@catalyst.net.nz>
This commit is contained in:
Andrew Bartlett 2015-10-14 16:57:31 +13:00
parent 3226077627
commit 145bb6fd7b

View File

@ -19,7 +19,9 @@
import ldb
from ldb import LdbError
from samba.ndr import ndr_unpack
from samba.dcerpc import misc
from samba.dcerpc import misc, dnsp
from samba.dcerpc.dnsp import DNS_TYPE_NS, DNS_TYPE_A, DNS_TYPE_AAAA, \
DNS_TYPE_CNAME, DNS_TYPE_SRV, DNS_TYPE_PTR
class DemoteException(Exception):
"""Base element for demote errors"""
@ -87,14 +89,106 @@ def remove_dns_references(samdb, dnsHostName):
if len(zones) == 0:
return
dnsHostNameUpper = dnsHostName.upper()
try:
rec = samdb.dns_lookup(dnsHostName)
primary_recs = samdb.dns_lookup(dnsHostName)
except RuntimeError as (enum, estr):
if enum == 0x000025F2: #WERR_DNS_ERROR_NAME_DOES_NOT_EXIST
return
raise DemoteException("lookup of %s failed: %s" % (dnsHostName, estr))
samdb.dns_replace(dnsHostName, [])
res = samdb.search("",
scope=ldb.SCOPE_BASE, attrs=["namingContexts"])
assert len(res) == 1
ncs = res[0]["namingContexts"]
# Work out the set of names we will likely have an A record on by
# default. This is by default all the partitions of type
# domainDNS. By finding the canocial name of all the partitions,
# we find the likely candidates. We only remove the record if it
# maches the IP that was used by the dnsHostName. This avoids us
# needing to look a the dns_update_list file from in the demote
# script.
def dns_name_from_dn(dn):
# The canonical string of DC=example,DC=com is
# example.com/
#
# The canonical string of CN=Configuration,DC=example,DC=com
# is example.com/Configuration
return ldb.Dn(samdb, dn).canonical_str().split('/', 1)[0]
# By using a set here, duplicates via (eg) example.com/Configuration
# do not matter, they become just example.com
a_names_to_remove_from \
= set(dns_name_from_dn(dn) 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" % \
(a_name, len(a_recs), orig_num_recs - len(a_recs))
samdb.dns_replace(a_name, a_recs)
# Find all the CNAME, NS, PTR and SRV records that point at the
# name we are removing
def to_remove(value):
dnsRecord = ndr_unpack(dnsp.DnssrvRpcRecord, value)
if dnsRecord.wType == DNS_TYPE_NS \
or dnsRecord.wType == DNS_TYPE_CNAME \
or dnsRecord.wType == DNS_TYPE_PTR:
if dnsRecord.data.upper() == dnsHostNameUpper:
return True
elif dnsRecord.wType == DNS_TYPE_SRV:
if dnsRecord.data.nameTarget.upper() == dnsHostNameUpper:
return True
return False
for zone in zones:
print "checking %s" % zone.dn
records = samdb.search(base=zone.dn, scope=ldb.SCOPE_SUBTREE,
expression="(&(objectClass=dnsNode)"
"(!(dNSTombstoned=TRUE)))",
attrs=["dnsRecord"])
for record in records:
try:
values = record["dnsRecord"]
except KeyError:
next
orig_num_values = len(values)
# Remove references to dnsHostName in A, AAAA, NS, CNAME and SRV
values = [ ndr_unpack(dnsp.DnssrvRpcRecord, v)
for v in values if not to_remove(v) ]
if len(values) != orig_num_values:
print "updating %s keeping %d values, removing %s values" \
% (record.dn, len(values), orig_num_values - len(values))
# This requires the values to be unpacked, so this
# has been done in the list comprehension above
samdb.dns_replace_by_dn(record.dn, values)
def offline_remove_server(samdb, server_dn,
remove_computer_obj=False,
remove_server_obj=False,