2015-09-14 06:56:52 +03:00
# Unix SMB/CIFS implementation.
# Copyright Matthieu Patou <mat@matws.net> 2011
# Copyright Andrew Bartlett <abartlet@samba.org> 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 <http://www.gnu.org/licenses/>.
#
2015-10-20 03:48:07 +03:00
import uuid
2015-09-14 06:56:52 +03:00
import ldb
2015-09-24 05:07:51 +03:00
from ldb import LdbError
2017-02-13 01:12:54 +03:00
from samba import werror
2015-09-14 06:56:52 +03:00
from samba . ndr import ndr_unpack
2015-10-14 06:57:31 +03:00
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
2015-09-14 06:56:52 +03:00
2018-07-30 09:20:39 +03:00
2015-09-24 05:07:51 +03:00
class DemoteException ( Exception ) :
""" Base element for demote errors """
def __init__ ( self , value ) :
self . value = value
def __str__ ( self ) :
return " DemoteException: " + self . value
2015-09-14 06:56:52 +03:00
2015-10-16 03:47:29 +03:00
def remove_sysvol_references ( samdb , logger , dc_name ) :
2015-10-23 03:12:03 +03:00
# DNs under the Configuration DN:
2015-09-14 06:56:52 +03:00
realm = samdb . domain_dns_name ( )
2015-10-23 03:05:24 +03:00
for s in ( " CN=Enterprise,CN=Microsoft System Volumes,CN=System " ,
" CN= %s ,CN=Microsoft System Volumes,CN=System " % realm ) :
2015-10-23 03:12:03 +03:00
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.
2022-09-30 01:50:30 +03:00
try :
dn . add_base ( samdb . get_config_basedn ( ) )
except ldb . LdbError :
2018-07-30 09:22:01 +03:00
raise DemoteException ( " Failed constructing DN %s by adding base %s "
2015-10-23 03:12:03 +03:00
% ( dn , samdb . get_config_basedn ( ) ) )
2022-09-30 01:50:30 +03:00
try :
dn . add_child ( " CN=X " )
except ldb . LdbError :
2018-07-30 09:22:01 +03:00
raise DemoteException ( " Failed constructing DN %s by adding child CN=X "
2018-07-30 09:15:34 +03:00
% ( dn ) )
2015-10-23 03:12:03 +03:00
dn . set_component ( 0 , " CN " , dc_name )
2015-10-23 03:05:24 +03:00
try :
2015-10-16 03:47:29 +03:00
logger . info ( " Removing Sysvol reference: %s " % dn )
2015-10-23 03:12:03 +03:00
samdb . delete ( dn )
2018-02-23 17:29:05 +03:00
except ldb . LdbError as e :
( enum , estr ) = e . args
2015-10-23 03:12:03 +03:00
if enum == ldb . ERR_NO_SUCH_OBJECT :
pass
else :
raise
# DNs under the Domain DN:
for s in ( " CN=Domain System Volumes (SYSVOL share),CN=File Replication Service,CN=System " ,
" CN=Topology,CN=Domain System Volume,CN=DFSR-GlobalSettings,CN=System " ) :
# This is verbose, but it is the safe, escape-proof way
# to add a base and add an arbitrary RDN.
dn = ldb . Dn ( samdb , s )
2022-09-30 01:50:30 +03:00
try :
dn . add_base ( samdb . get_default_basedn ( ) )
except ldb . LdbError :
2018-10-26 11:02:46 +03:00
raise DemoteException ( " Failed constructing DN %s by adding base %s "
% ( dn , samdb . get_default_basedn ( ) ) )
2022-09-30 01:50:30 +03:00
try :
dn . add_child ( " CN=X " )
except ldb . LdbError :
2017-03-09 04:53:46 +03:00
raise DemoteException ( " Failed constructing DN %s by adding child "
" CN=X (soon to be CN= %s ) " % ( dn , dc_name ) )
2015-10-23 03:12:03 +03:00
dn . set_component ( 0 , " CN " , dc_name )
2015-10-16 03:47:29 +03:00
2015-09-14 06:56:52 +03:00
try :
2015-10-16 03:47:29 +03:00
logger . info ( " Removing Sysvol reference: %s " % dn )
2015-10-23 03:12:03 +03:00
samdb . delete ( dn )
2018-02-23 17:29:05 +03:00
except ldb . LdbError as e1 :
( enum , estr ) = e1 . args
2015-10-23 03:12:03 +03:00
if enum == ldb . ERR_NO_SUCH_OBJECT :
pass
else :
raise
2015-09-14 06:56:52 +03:00
2018-01-31 01:52:34 +03:00
def remove_dns_references ( samdb , logger , dnsHostName , ignore_no_name = False ) :
2015-09-14 06:56:52 +03:00
# 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
2015-10-14 06:57:31 +03:00
dnsHostNameUpper = dnsHostName . upper ( )
2015-09-14 06:56:52 +03:00
try :
2017-02-27 07:09:56 +03:00
( dn , primary_recs ) = samdb . dns_lookup ( dnsHostName )
2018-02-23 17:29:05 +03:00
except RuntimeError as e4 :
( enum , estr ) = e4 . args
2018-06-11 02:18:09 +03:00
if ( enum == werror . WERR_DNS_ERROR_NAME_DOES_NOT_EXIST or
enum == werror . WERR_DNS_ERROR_RCODE_NAME_ERROR ) :
2018-01-31 01:52:34 +03:00
if ignore_no_name :
remove_hanging_dns_references ( samdb , logger ,
dnsHostNameUpper ,
zones )
return
2015-09-24 05:07:51 +03:00
raise DemoteException ( " lookup of %s failed: %s " % ( dnsHostName , estr ) )
2015-09-14 06:56:52 +03:00
samdb . dns_replace ( dnsHostName , [ ] )
2015-10-14 06:57:31 +03:00
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
2023-08-29 05:23:51 +03:00
# domainDNS. By finding the canonical name of all the partitions,
2015-10-14 06:57:31 +03:00
# we find the likely candidates. We only remove the record if it
2023-06-06 14:31:52 +03:00
# matches the IP that was used by the dnsHostName. This avoids us
2023-08-29 05:23:51 +03:00
# needing to look at a dns_update_list file from in the demote
2015-10-14 06:57:31 +03:00
# 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 \
2018-04-26 20:22:21 +03:00
= set ( dns_name_from_dn ( str ( dn ) ) for dn in ncs )
2015-10-14 06:57:31 +03:00
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 )
2017-02-27 07:09:56 +03:00
( a_rec_dn , a_recs ) = samdb . dns_lookup ( a_name )
2018-02-23 17:29:05 +03:00
except RuntimeError as e2 :
( enum , estr ) = e2 . args
2017-02-13 01:12:54 +03:00
if enum == werror . WERR_DNS_ERROR_NAME_DOES_NOT_EXIST :
2015-10-14 06:57:31 +03:00
return
raise DemoteException ( " lookup of %s failed: %s " % ( a_name , estr ) )
orig_num_recs = len ( a_recs )
2018-07-30 09:17:02 +03:00
a_recs = [ r for r in a_recs if not a_rec_to_remove ( r ) ]
2015-10-14 06:57:31 +03:00
if len ( a_recs ) != orig_num_recs :
2018-07-30 09:22:01 +03:00
logger . info ( " updating %s keeping %d values, removing %s values " %
2018-07-30 09:16:12 +03:00
( a_name , len ( a_recs ) , orig_num_recs - len ( a_recs ) ) )
2015-10-14 06:57:31 +03:00
samdb . dns_replace ( a_name , a_recs )
2018-01-31 01:52:34 +03:00
remove_hanging_dns_references ( samdb , logger , dnsHostNameUpper , zones )
def remove_hanging_dns_references ( samdb , logger , dnsHostNameUpper , zones ) :
2015-10-14 06:57:31 +03:00
# 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 :
2015-10-16 03:47:29 +03:00
logger . debug ( " checking %s " % zone . dn )
2015-10-14 06:57:31 +03:00
records = samdb . search ( base = zone . dn , scope = ldb . SCOPE_SUBTREE ,
expression = " (&(objectClass=dnsNode) "
" (!(dNSTombstoned=TRUE))) " ,
attrs = [ " dnsRecord " ] )
for record in records :
try :
python/remove_dc: handle dnsNode objects without dnsRecord attribute
If we have dnsNode objects without dnsRecord attribute values we trigger
the following error triggered by 'samba-tool domain demote --remove-other-dead-server=server2'
ERROR(<type 'exceptions.TypeError'>): uncaught exception - __ndr_unpack__()
argument 1 must be string or read-only buffer, not dnsp.DnssrvRpcRecord
File "/usr/lib64/python2.6/site-packages/samba/netcmd/__init__.py", line 175,
in _run
return self.run(*args, **kwargs)
File "/usr/lib64/python2.6/site-packages/samba/netcmd/domain.py", line 720, in
run
remove_dc.remove_dc(samdb, logger, remove_other_dead_server)
File "/usr/lib64/python2.6/site-packages/samba/remove_dc.py", line 423, in
remove_dc
remove_dns_account=True)
File "/usr/lib64/python2.6/site-packages/samba/remove_dc.py", line 351, in
offline_remove_ntds_dc
remove_dns_account=remove_dns_account)
File "/usr/lib64/python2.6/site-packages/samba/remove_dc.py", line 266, in
offline_remove_server
remove_dns_references(samdb, logger, dnsHostName)
File "/usr/lib64/python2.6/site-packages/samba/remove_dc.py", line 186, in
remove_dns_references
for v in values if not to_remove(v) ]
File "/usr/lib64/python2.6/site-packages/samba/remove_dc.py", line 160, in
to_remove
dnsRecord = ndr_unpack(dnsp.DnssrvRpcRecord, value)
File "/usr/lib64/python2.6/site-packages/samba/ndr.py", line 45, in ndr_unpack
object.__ndr_unpack__(data, allow_remaining=allow_remaining)
A transaction is still active in ldb context [0xe1f320] on
tdb:///var/lib/samba/private/sam.ldb
"next" is used in perl not in python!
BUG: https://bugzilla.samba.org/show_bug.cgi?id=12018
Signed-off-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
Autobuild-User(master): Andrew Bartlett <abartlet@samba.org>
Autobuild-Date(master): Wed Jul 13 10:10:30 CEST 2016 on sn-devel-144
2016-07-11 16:25:31 +03:00
orig_values = record [ " dnsRecord " ]
2015-10-14 06:57:31 +03:00
except KeyError :
python/remove_dc: handle dnsNode objects without dnsRecord attribute
If we have dnsNode objects without dnsRecord attribute values we trigger
the following error triggered by 'samba-tool domain demote --remove-other-dead-server=server2'
ERROR(<type 'exceptions.TypeError'>): uncaught exception - __ndr_unpack__()
argument 1 must be string or read-only buffer, not dnsp.DnssrvRpcRecord
File "/usr/lib64/python2.6/site-packages/samba/netcmd/__init__.py", line 175,
in _run
return self.run(*args, **kwargs)
File "/usr/lib64/python2.6/site-packages/samba/netcmd/domain.py", line 720, in
run
remove_dc.remove_dc(samdb, logger, remove_other_dead_server)
File "/usr/lib64/python2.6/site-packages/samba/remove_dc.py", line 423, in
remove_dc
remove_dns_account=True)
File "/usr/lib64/python2.6/site-packages/samba/remove_dc.py", line 351, in
offline_remove_ntds_dc
remove_dns_account=remove_dns_account)
File "/usr/lib64/python2.6/site-packages/samba/remove_dc.py", line 266, in
offline_remove_server
remove_dns_references(samdb, logger, dnsHostName)
File "/usr/lib64/python2.6/site-packages/samba/remove_dc.py", line 186, in
remove_dns_references
for v in values if not to_remove(v) ]
File "/usr/lib64/python2.6/site-packages/samba/remove_dc.py", line 160, in
to_remove
dnsRecord = ndr_unpack(dnsp.DnssrvRpcRecord, value)
File "/usr/lib64/python2.6/site-packages/samba/ndr.py", line 45, in ndr_unpack
object.__ndr_unpack__(data, allow_remaining=allow_remaining)
A transaction is still active in ldb context [0xe1f320] on
tdb:///var/lib/samba/private/sam.ldb
"next" is used in perl not in python!
BUG: https://bugzilla.samba.org/show_bug.cgi?id=12018
Signed-off-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
Autobuild-User(master): Andrew Bartlett <abartlet@samba.org>
Autobuild-Date(master): Wed Jul 13 10:10:30 CEST 2016 on sn-devel-144
2016-07-11 16:25:31 +03:00
continue
2015-10-14 06:57:31 +03:00
# Remove references to dnsHostName in A, AAAA, NS, CNAME and SRV
2018-07-30 09:16:43 +03:00
values = [ ndr_unpack ( dnsp . DnssrvRpcRecord , v )
2018-09-03 16:05:48 +03:00
for v in orig_values if not to_remove ( v ) ]
2015-10-14 06:57:31 +03:00
python/remove_dc: handle dnsNode objects without dnsRecord attribute
If we have dnsNode objects without dnsRecord attribute values we trigger
the following error triggered by 'samba-tool domain demote --remove-other-dead-server=server2'
ERROR(<type 'exceptions.TypeError'>): uncaught exception - __ndr_unpack__()
argument 1 must be string or read-only buffer, not dnsp.DnssrvRpcRecord
File "/usr/lib64/python2.6/site-packages/samba/netcmd/__init__.py", line 175,
in _run
return self.run(*args, **kwargs)
File "/usr/lib64/python2.6/site-packages/samba/netcmd/domain.py", line 720, in
run
remove_dc.remove_dc(samdb, logger, remove_other_dead_server)
File "/usr/lib64/python2.6/site-packages/samba/remove_dc.py", line 423, in
remove_dc
remove_dns_account=True)
File "/usr/lib64/python2.6/site-packages/samba/remove_dc.py", line 351, in
offline_remove_ntds_dc
remove_dns_account=remove_dns_account)
File "/usr/lib64/python2.6/site-packages/samba/remove_dc.py", line 266, in
offline_remove_server
remove_dns_references(samdb, logger, dnsHostName)
File "/usr/lib64/python2.6/site-packages/samba/remove_dc.py", line 186, in
remove_dns_references
for v in values if not to_remove(v) ]
File "/usr/lib64/python2.6/site-packages/samba/remove_dc.py", line 160, in
to_remove
dnsRecord = ndr_unpack(dnsp.DnssrvRpcRecord, value)
File "/usr/lib64/python2.6/site-packages/samba/ndr.py", line 45, in ndr_unpack
object.__ndr_unpack__(data, allow_remaining=allow_remaining)
A transaction is still active in ldb context [0xe1f320] on
tdb:///var/lib/samba/private/sam.ldb
"next" is used in perl not in python!
BUG: https://bugzilla.samba.org/show_bug.cgi?id=12018
Signed-off-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
Autobuild-User(master): Andrew Bartlett <abartlet@samba.org>
Autobuild-Date(master): Wed Jul 13 10:10:30 CEST 2016 on sn-devel-144
2016-07-11 16:25:31 +03:00
if len ( values ) != len ( orig_values ) :
2018-07-30 09:22:01 +03:00
logger . info ( " updating %s keeping %d values, removing %s values "
2015-10-16 03:47:29 +03:00
% ( record . dn , len ( values ) ,
python/remove_dc: handle dnsNode objects without dnsRecord attribute
If we have dnsNode objects without dnsRecord attribute values we trigger
the following error triggered by 'samba-tool domain demote --remove-other-dead-server=server2'
ERROR(<type 'exceptions.TypeError'>): uncaught exception - __ndr_unpack__()
argument 1 must be string or read-only buffer, not dnsp.DnssrvRpcRecord
File "/usr/lib64/python2.6/site-packages/samba/netcmd/__init__.py", line 175,
in _run
return self.run(*args, **kwargs)
File "/usr/lib64/python2.6/site-packages/samba/netcmd/domain.py", line 720, in
run
remove_dc.remove_dc(samdb, logger, remove_other_dead_server)
File "/usr/lib64/python2.6/site-packages/samba/remove_dc.py", line 423, in
remove_dc
remove_dns_account=True)
File "/usr/lib64/python2.6/site-packages/samba/remove_dc.py", line 351, in
offline_remove_ntds_dc
remove_dns_account=remove_dns_account)
File "/usr/lib64/python2.6/site-packages/samba/remove_dc.py", line 266, in
offline_remove_server
remove_dns_references(samdb, logger, dnsHostName)
File "/usr/lib64/python2.6/site-packages/samba/remove_dc.py", line 186, in
remove_dns_references
for v in values if not to_remove(v) ]
File "/usr/lib64/python2.6/site-packages/samba/remove_dc.py", line 160, in
to_remove
dnsRecord = ndr_unpack(dnsp.DnssrvRpcRecord, value)
File "/usr/lib64/python2.6/site-packages/samba/ndr.py", line 45, in ndr_unpack
object.__ndr_unpack__(data, allow_remaining=allow_remaining)
A transaction is still active in ldb context [0xe1f320] on
tdb:///var/lib/samba/private/sam.ldb
"next" is used in perl not in python!
BUG: https://bugzilla.samba.org/show_bug.cgi?id=12018
Signed-off-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
Autobuild-User(master): Andrew Bartlett <abartlet@samba.org>
Autobuild-Date(master): Wed Jul 13 10:10:30 CEST 2016 on sn-devel-144
2016-07-11 16:25:31 +03:00
len ( orig_values ) - len ( values ) ) )
2015-10-14 06:57:31 +03:00
# 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 )
2018-01-31 01:52:34 +03:00
2015-10-16 03:47:29 +03:00
def offline_remove_server ( samdb , logger ,
server_dn ,
2015-09-24 05:07:51 +03:00
remove_computer_obj = False ,
remove_server_obj = False ,
remove_sysvol_obj = False ,
2015-10-16 03:00:20 +03:00
remove_dns_names = False ,
remove_dns_account = False ) :
2015-09-14 06:56:52 +03:00
res = samdb . search ( " " ,
scope = ldb . SCOPE_BASE , attrs = [ " dsServiceName " ] )
assert len ( res ) == 1
my_serviceName = res [ 0 ] [ " dsServiceName " ] [ 0 ]
# 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 ]
2018-10-11 03:07:30 +03:00
dc_name = str ( msg [ " cn " ] [ 0 ] )
2015-09-14 06:56:52 +03:00
try :
2018-10-11 03:07:30 +03:00
computer_dn = ldb . Dn ( samdb , msg [ " serverReference " ] [ 0 ] . decode ( ' utf8 ' ) )
2015-09-14 06:56:52 +03:00
except KeyError :
computer_dn = None
try :
2018-10-11 03:07:30 +03:00
dnsHostName = str ( msg [ " dnsHostName " ] [ 0 ] )
2015-09-14 06:56:52 +03:00
except KeyError :
dnsHostName = None
if remove_server_obj :
2018-06-15 02:54:37 +03:00
# Remove the server DN (do a tree-delete as it could still have a
# 'DNS Settings' child object if it's a Windows DC)
samdb . delete ( server_dn , [ " tree_delete:0 " ] )
2015-09-14 06:56:52 +03:00
if computer_dn is not None :
computer_msgs = samdb . search ( base = computer_dn ,
expression = " objectclass=computer " ,
attrs = [ " msDS-KrbTgtLink " ,
2015-10-16 03:00:20 +03:00
" rIDSetReferences " ,
" cn " ] ,
2015-09-14 06:56:52 +03:00
scope = ldb . SCOPE_BASE )
if " rIDSetReferences " in computer_msgs [ 0 ] :
2015-10-16 03:47:29 +03:00
rid_set_dn = str ( computer_msgs [ 0 ] [ " rIDSetReferences " ] [ 0 ] )
logger . info ( " Removing RID Set: %s " % rid_set_dn )
samdb . delete ( rid_set_dn )
2015-09-14 06:56:52 +03:00
if " msDS-KrbTgtLink " in computer_msgs [ 0 ] :
2015-10-16 03:47:29 +03:00
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 )
2015-09-14 06:56:52 +03:00
if remove_computer_obj :
# Delete the computer tree
2015-10-16 03:47:29 +03:00
logger . info ( " Removing computer account: %s (and any child objects) " % computer_dn )
2015-09-14 06:56:52 +03:00
samdb . delete ( computer_dn , [ " tree_delete:0 " ] )
2018-10-11 03:07:30 +03:00
if " dnsHostName " in msg :
dnsHostName = str ( msg [ " dnsHostName " ] [ 0 ] )
2015-09-14 06:56:52 +03:00
2015-10-16 03:00:20 +03:00
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 :
2015-10-16 03:47:29 +03:00
logger . info ( " Removing Samba-specific DNS service account: %s " % res [ 0 ] . dn )
2015-10-16 03:00:20 +03:00
samdb . delete ( res [ 0 ] . dn )
2015-09-14 06:56:52 +03:00
if dnsHostName is not None and remove_dns_names :
2015-10-16 03:47:29 +03:00
remove_dns_references ( samdb , logger , dnsHostName )
2015-09-14 06:56:52 +03:00
if remove_sysvol_obj :
2015-10-16 03:47:29 +03:00
remove_sysvol_references ( samdb , logger , dc_name )
2015-09-14 06:56:52 +03:00
2018-07-30 09:20:39 +03:00
2015-10-16 03:47:29 +03:00
def offline_remove_ntds_dc ( samdb ,
logger ,
ntds_dn ,
2015-09-24 05:07:51 +03:00
remove_computer_obj = False ,
remove_server_obj = False ,
remove_connection_obj = False ,
seize_stale_fsmo = False ,
remove_sysvol_obj = False ,
2015-10-16 03:00:20 +03:00
remove_dns_names = False ,
remove_dns_account = False ) :
2015-09-24 05:07:51 +03:00
res = samdb . search ( " " ,
scope = ldb . SCOPE_BASE , attrs = [ " dsServiceName " ] )
assert len ( res ) == 1
2018-04-25 22:01:49 +03:00
my_serviceName = ldb . Dn ( samdb , res [ 0 ] [ " dsServiceName " ] [ 0 ] . decode ( ' utf8 ' ) )
2015-09-24 05:07:51 +03:00
server_dn = ntds_dn . parent ( )
2015-10-13 05:26:20 +03:00
if my_serviceName == ntds_dn :
raise DemoteException ( " Refusing to demote our own DSA: %s " % my_serviceName )
2015-09-24 05:07:51 +03:00
try :
msgs = samdb . search ( base = ntds_dn , expression = " objectClass=ntdsDSA " ,
2018-07-30 09:16:12 +03:00
attrs = [ " objectGUID " ] , scope = ldb . SCOPE_BASE )
2018-02-23 17:29:05 +03:00
except LdbError as e5 :
( enum , estr ) = e5 . args
2015-09-24 05:07:51 +03:00
if enum == ldb . ERR_NO_SUCH_OBJECT :
2018-07-30 09:13:57 +03:00
raise DemoteException ( " Given DN %s doesn ' t exist " % ntds_dn )
2015-09-24 05:07:51 +03:00
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=<GUID= %s >)) " % ntds_guid )
for conn in stale_connections :
2015-10-16 03:47:29 +03:00
logger . info ( " Removing nTDSConnection: %s " % conn . dn )
2015-09-24 05:07:51 +03:00
samdb . delete ( conn . dn )
if seize_stale_fsmo :
stale_fsmo_roles = samdb . search ( base = " " , scope = ldb . SCOPE_SUBTREE ,
expression = " (fsmoRoleOwner=<GUID= %s >)) "
% 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 ' )
2015-10-16 03:47:29 +03:00
logger . warning ( " Seizing FSMO role on: %s (now owned by %s ) "
% ( role . dn , my_serviceName ) )
2015-09-24 05:07:51 +03:00
samdb . modify ( m )
# Remove the NTDS setting tree
try :
2015-10-16 03:47:29 +03:00
logger . info ( " Removing nTDSDSA: %s (and any children) " % ntds_dn )
2015-09-24 05:07:51 +03:00
samdb . delete ( ntds_dn , [ " tree_delete:0 " ] )
2018-02-23 17:29:05 +03:00
except LdbError as e6 :
( enum , estr ) = e6 . args
2015-09-24 05:07:51 +03:00
raise DemoteException ( " Failed to remove the DCs NTDS DSA object: %s "
% estr )
2015-10-16 03:47:29 +03:00
offline_remove_server ( samdb , logger , server_dn ,
2015-09-24 05:07:51 +03:00
remove_computer_obj = remove_computer_obj ,
remove_server_obj = remove_server_obj ,
remove_sysvol_obj = remove_sysvol_obj ,
2015-10-16 03:00:20 +03:00
remove_dns_names = remove_dns_names ,
remove_dns_account = remove_dns_account )
2015-09-24 05:07:51 +03:00
2015-09-14 06:56:52 +03:00
2015-10-16 03:47:29 +03:00
def remove_dc ( samdb , logger , dc_name ) :
2015-09-14 06:56:52 +03:00
2015-10-13 05:26:20 +03:00
# TODO: Check if this is the last server (covered mostly by
# refusing to remove our own name)
2015-09-14 06:56:52 +03:00
samdb . transaction_start ( )
2015-10-20 03:48:07 +03:00
server_dn = None
2015-09-14 06:56:52 +03:00
2023-08-29 05:23:51 +03:00
# Allow the name to be an nTDS-DSA GUID
2015-10-20 03:48:07 +03:00
try :
ntds_guid = uuid . UUID ( hex = dc_name )
ntds_dn = " <GUID= %s > " % ntds_guid
except ValueError :
try :
server_msgs = samdb . search ( base = samdb . get_config_basedn ( ) ,
attrs = [ ] ,
expression = " (&(objectClass=server) "
" (cn= %s )) "
2018-07-30 09:16:12 +03:00
% ldb . binary_encode ( dc_name ) )
2018-02-23 17:29:05 +03:00
except LdbError as e3 :
( enum , estr ) = e3 . args
2015-10-20 03:48:07 +03:00
raise DemoteException ( " Failure checking if %s is an server "
2018-10-26 11:03:21 +03:00
" object in %s : %s "
% ( dc_name , samdb . domain_dns_name ( ) , estr ) )
2015-10-20 03:48:07 +03:00
if ( len ( server_msgs ) == 0 ) :
2018-01-18 23:16:04 +03:00
samdb . transaction_cancel ( )
2015-10-20 03:48:07 +03:00
raise DemoteException ( " %s is not an AD DC in %s "
% ( dc_name , samdb . domain_dns_name ( ) ) )
server_dn = server_msgs [ 0 ] . dn
ntds_dn = ldb . Dn ( samdb , " CN=NTDS Settings " )
ntds_dn . add_base ( server_dn )
2015-09-14 06:56:52 +03:00
# Confirm this is really an ntdsDSA object
2015-09-24 05:07:51 +03:00
try :
2015-10-20 03:48:07 +03:00
ntds_msgs = samdb . search ( base = ntds_dn , attrs = [ ] , scope = ldb . SCOPE_BASE ,
expression = " (objectClass=ntdsdsa) " )
2018-02-23 17:29:05 +03:00
except LdbError as e7 :
( enum , estr ) = e7 . args
2015-09-24 05:07:51 +03:00
if enum == ldb . ERR_NO_SUCH_OBJECT :
2015-10-20 03:48:07 +03:00
ntds_msgs = [ ]
else :
2018-01-18 23:16:04 +03:00
samdb . transaction_cancel ( )
2018-10-26 11:03:21 +03:00
raise DemoteException (
" Failure checking if %s is an NTDS DSA in %s : %s " %
( ntds_dn , samdb . domain_dns_name ( ) , estr ) )
2015-10-20 03:48:07 +03:00
2023-06-06 14:31:52 +03:00
# If the NTDS Settings child DN wasn't found or wasn't an ntdsDSA
2015-10-20 03:48:07 +03:00
# object, just remove the server object located above
if ( len ( ntds_msgs ) == 0 ) :
if server_dn is None :
2018-01-18 23:16:04 +03:00
samdb . transaction_cancel ( )
2015-10-20 03:48:07 +03:00
raise DemoteException ( " %s is not an AD DC in %s "
% ( dc_name , samdb . domain_dns_name ( ) ) )
offline_remove_server ( samdb , logger ,
server_dn ,
remove_computer_obj = True ,
remove_server_obj = True ,
remove_sysvol_obj = True ,
remove_dns_names = True ,
remove_dns_account = True )
else :
offline_remove_ntds_dc ( samdb , logger ,
ntds_msgs [ 0 ] . dn ,
remove_computer_obj = True ,
remove_server_obj = True ,
remove_connection_obj = True ,
seize_stale_fsmo = True ,
remove_sysvol_obj = True ,
remove_dns_names = True ,
remove_dns_account = True )
2015-09-14 06:56:52 +03:00
samdb . transaction_commit ( )
def offline_remove_dc_RemoveDsServer ( samdb , ntds_dn ) :
samdb . start_transaction ( )
2015-10-16 03:47:29 +03:00
offline_remove_ntds_dc ( samdb , ntds_dn , None )
2015-09-14 06:56:52 +03:00
samdb . commit_transaction ( )