2010-08-26 08:35:30 +04:00
# DRS utility code
#
# Copyright Andrew Tridgell 2010
2017-01-27 00:18:21 +03:00
# Copyright Andrew Bartlett 2017
2010-08-26 08:35:30 +04:00
#
# 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/>.
#
2017-02-17 08:22:19 +03:00
from samba . dcerpc import drsuapi , misc , drsblobs
2010-08-26 08:35:30 +04:00
from samba . net import Net
2017-02-17 08:22:19 +03:00
from samba . ndr import ndr_unpack
from samba import dsdb
2017-06-16 03:54:32 +03:00
from samba import werror
from samba import WERRORError
2018-07-30 09:21:38 +03:00
import samba
import ldb
2018-11-05 06:54:05 +03:00
from samba . dcerpc . drsuapi import ( DRSUAPI_ATTID_name ,
DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V8 ,
DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V10 )
2018-06-06 01:04:29 +03:00
import re
2010-09-29 07:46:15 +04:00
2018-07-30 09:20:39 +03:00
2011-12-08 23:23:57 +04:00
class drsException ( Exception ) :
2011-11-28 23:48:59 +04:00
""" Base element for drs errors """
def __init__ ( self , value ) :
self . value = value
def __str__ ( self ) :
return " drsException: " + self . value
2022-11-01 02:34:57 +03:00
def drsuapi_connect ( server , lp , creds , ip = None ) :
2011-12-08 23:23:57 +04:00
""" Make a DRSUAPI connection to the server.
2011-11-28 23:48:59 +04:00
: param server : the name of the server to connect to
: param lp : a samba line parameter object
: param creds : credential used for the connection
2022-11-01 02:34:57 +03:00
: param ip : Forced target server name
2011-11-28 23:48:59 +04:00
: return : A tuple with the drsuapi bind object , the drsuapi handle
and the supported extensions .
: raise drsException : if the connection fails
"""
binding_options = " seal "
2017-09-06 07:40:05 +03:00
if lp . log_level ( ) > = 9 :
2011-11-28 23:48:59 +04:00
binding_options + = " ,print "
2022-11-01 02:34:57 +03:00
# Allow forcing the IP
if ip is not None :
binding_options + = f " ,target_hostname= { server } "
binding_string = f " ncacn_ip_tcp: { ip } [ { binding_options } ] "
else :
binding_string = " ncacn_ip_tcp: %s [ %s ] " % ( server , binding_options )
2011-11-28 23:48:59 +04:00
try :
drsuapiBind = drsuapi . drsuapi ( binding_string , lp , creds )
( drsuapiHandle , bindSupportedExtensions ) = drs_DsBind ( drsuapiBind )
2018-02-14 00:18:36 +03:00
except Exception as e :
2011-12-05 02:00:57 +04:00
raise drsException ( " DRS connection to %s failed: %s " % ( server , e ) )
2011-11-28 23:48:59 +04:00
return ( drsuapiBind , drsuapiHandle , bindSupportedExtensions )
2011-12-08 23:23:57 +04:00
def sendDsReplicaSync ( drsuapiBind , drsuapi_handle , source_dsa_guid ,
2018-07-30 09:16:12 +03:00
naming_context , req_option ) :
2011-12-08 23:23:57 +04:00
""" Send DS replica sync request.
2011-11-28 23:48:59 +04:00
: param drsuapiBind : a drsuapi Bind object
2018-06-08 06:36:39 +03:00
: param drsuapi_handle : a drsuapi handle on the drsuapi connection
2011-11-28 23:48:59 +04:00
: param source_dsa_guid : the guid of the source dsa for the replication
: param naming_context : the DN of the naming context to replicate
: param req_options : replication options for the DsReplicaSync call
2011-12-08 23:23:57 +04:00
: raise drsException : if any error occur while sending and receiving the
reply for the dsReplicaSync
2011-11-28 23:48:59 +04:00
"""
nc = drsuapi . DsReplicaObjectIdentifier ( )
nc . dn = naming_context
req1 = drsuapi . DsReplicaSyncRequest1 ( )
2018-07-30 09:22:11 +03:00
req1 . naming_context = nc
2011-11-28 23:48:59 +04:00
req1 . options = req_option
req1 . source_dsa_guid = misc . GUID ( source_dsa_guid )
try :
drsuapiBind . DsReplicaSync ( drsuapi_handle , 1 , req1 )
2018-02-14 00:18:36 +03:00
except Exception as estr :
2011-11-28 23:48:59 +04:00
raise drsException ( " DsReplicaSync failed %s " % estr )
2011-12-08 23:23:57 +04:00
2010-09-29 07:46:15 +04:00
def drs_DsBind ( drs ) :
''' make a DsBind call, returning the binding handle '''
bind_info = drsuapi . DsBindInfoCtr ( )
bind_info . length = 28
bind_info . info = drsuapi . DsBindInfo28 ( )
2010-11-28 06:22:46 +03:00
bind_info . info . supported_extensions | = drsuapi . DRSUAPI_SUPPORTED_EXTENSION_BASE
bind_info . info . supported_extensions | = drsuapi . DRSUAPI_SUPPORTED_EXTENSION_ASYNC_REPLICATION
bind_info . info . supported_extensions | = drsuapi . DRSUAPI_SUPPORTED_EXTENSION_REMOVEAPI
bind_info . info . supported_extensions | = drsuapi . DRSUAPI_SUPPORTED_EXTENSION_MOVEREQ_V2
bind_info . info . supported_extensions | = drsuapi . DRSUAPI_SUPPORTED_EXTENSION_GETCHG_COMPRESS
bind_info . info . supported_extensions | = drsuapi . DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V1
bind_info . info . supported_extensions | = drsuapi . DRSUAPI_SUPPORTED_EXTENSION_RESTORE_USN_OPTIMIZATION
bind_info . info . supported_extensions | = drsuapi . DRSUAPI_SUPPORTED_EXTENSION_KCC_EXECUTE
bind_info . info . supported_extensions | = drsuapi . DRSUAPI_SUPPORTED_EXTENSION_ADDENTRY_V2
bind_info . info . supported_extensions | = drsuapi . DRSUAPI_SUPPORTED_EXTENSION_LINKED_VALUE_REPLICATION
bind_info . info . supported_extensions | = drsuapi . DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V2
bind_info . info . supported_extensions | = drsuapi . DRSUAPI_SUPPORTED_EXTENSION_INSTANCE_TYPE_NOT_REQ_ON_MOD
bind_info . info . supported_extensions | = drsuapi . DRSUAPI_SUPPORTED_EXTENSION_CRYPTO_BIND
bind_info . info . supported_extensions | = drsuapi . DRSUAPI_SUPPORTED_EXTENSION_GET_REPL_INFO
bind_info . info . supported_extensions | = drsuapi . DRSUAPI_SUPPORTED_EXTENSION_STRONG_ENCRYPTION
bind_info . info . supported_extensions | = drsuapi . DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V01
bind_info . info . supported_extensions | = drsuapi . DRSUAPI_SUPPORTED_EXTENSION_TRANSITIVE_MEMBERSHIP
bind_info . info . supported_extensions | = drsuapi . DRSUAPI_SUPPORTED_EXTENSION_ADD_SID_HISTORY
bind_info . info . supported_extensions | = drsuapi . DRSUAPI_SUPPORTED_EXTENSION_POST_BETA3
bind_info . info . supported_extensions | = drsuapi . DRSUAPI_SUPPORTED_EXTENSION_GET_MEMBERSHIPS2
bind_info . info . supported_extensions | = drsuapi . DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V6
bind_info . info . supported_extensions | = drsuapi . DRSUAPI_SUPPORTED_EXTENSION_NONDOMAIN_NCS
bind_info . info . supported_extensions | = drsuapi . DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V8
bind_info . info . supported_extensions | = drsuapi . DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V5
bind_info . info . supported_extensions | = drsuapi . DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V6
bind_info . info . supported_extensions | = drsuapi . DRSUAPI_SUPPORTED_EXTENSION_ADDENTRYREPLY_V3
bind_info . info . supported_extensions | = drsuapi . DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V7
bind_info . info . supported_extensions | = drsuapi . DRSUAPI_SUPPORTED_EXTENSION_VERIFY_OBJECT
2010-09-29 07:46:15 +04:00
( info , handle ) = drs . DsBind ( misc . GUID ( drsuapi . DRSUAPI_DS_BIND_GUID ) , bind_info )
2010-11-07 05:55:20 +03:00
return ( handle , info . info . supported_extensions )
2010-09-29 07:46:15 +04:00
2010-11-28 06:22:46 +03:00
getncchanges script: use library code, not copied functions.
These functions were duplicates. To be exact, the diff -ub between what
getncchanges had, and what drs_uitls now has is this:
|@@ -1,4 +1,5 @@
|-def do_DsBind(drs):
|+def drs_DsBind(drs):
| '''make a DsBind call, returning the binding handle'''
| bind_info = drsuapi.DsBindInfoCtr()
| bind_info.length = 28
|@@ -32,7 +33,8 @@
| bind_info.info.supported_extensions |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V7
| bind_info.info.supported_extensions |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_VERIFY_OBJECT
| (info, handle) = drs.DsBind(misc.GUID(drsuapi.DRSUAPI_DS_BIND_GUID), bind_info)
|- return handle
|+
|+ return (handle, info.info.supported_extensions)
|
|
| def drs_get_rodc_partial_attribute_set(samdb):
|@@ -43,7 +45,7 @@
| attids = []
|
| # the exact list of attids we send is quite critical. Note that
|- # we do ask for the secret attributes, but set set SPECIAL_SECRET_PROCESSING
|+ # we do ask for the secret attributes, but set SPECIAL_SECRET_PROCESSING
| # to zero them out
| schema_dn = samdb.get_schema_basedn()
| res = samdb.search(base=schema_dn, scope=ldb.SCOPE_SUBTREE,
|@@ -71,3 +73,4 @@
| partial_attribute_set.attids = attids
| partial_attribute_set.num_attids = len(attids)
| return partial_attribute_set
while the drs_utils code has changed in moving
drs_get_rodc_partial_attribute_set() out of the class.
Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
2016-10-28 05:05:28 +03:00
def drs_get_rodc_partial_attribute_set ( samdb ) :
''' get a list of attributes for RODC replication '''
partial_attribute_set = drsuapi . DsPartialAttributeSet ( )
partial_attribute_set . version = 1
attids = [ ]
# the exact list of attids we send is quite critical. Note that
# we do ask for the secret attributes, but set SPECIAL_SECRET_PROCESSING
# to zero them out
schema_dn = samdb . get_schema_basedn ( )
res = samdb . search ( base = schema_dn , scope = ldb . SCOPE_SUBTREE ,
expression = " objectClass=attributeSchema " ,
attrs = [ " lDAPDisplayName " , " systemFlags " ,
" searchFlags " ] )
for r in res :
2018-09-03 19:56:56 +03:00
ldap_display_name = str ( r [ " lDAPDisplayName " ] [ 0 ] )
getncchanges script: use library code, not copied functions.
These functions were duplicates. To be exact, the diff -ub between what
getncchanges had, and what drs_uitls now has is this:
|@@ -1,4 +1,5 @@
|-def do_DsBind(drs):
|+def drs_DsBind(drs):
| '''make a DsBind call, returning the binding handle'''
| bind_info = drsuapi.DsBindInfoCtr()
| bind_info.length = 28
|@@ -32,7 +33,8 @@
| bind_info.info.supported_extensions |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V7
| bind_info.info.supported_extensions |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_VERIFY_OBJECT
| (info, handle) = drs.DsBind(misc.GUID(drsuapi.DRSUAPI_DS_BIND_GUID), bind_info)
|- return handle
|+
|+ return (handle, info.info.supported_extensions)
|
|
| def drs_get_rodc_partial_attribute_set(samdb):
|@@ -43,7 +45,7 @@
| attids = []
|
| # the exact list of attids we send is quite critical. Note that
|- # we do ask for the secret attributes, but set set SPECIAL_SECRET_PROCESSING
|+ # we do ask for the secret attributes, but set SPECIAL_SECRET_PROCESSING
| # to zero them out
| schema_dn = samdb.get_schema_basedn()
| res = samdb.search(base=schema_dn, scope=ldb.SCOPE_SUBTREE,
|@@ -71,3 +73,4 @@
| partial_attribute_set.attids = attids
| partial_attribute_set.num_attids = len(attids)
| return partial_attribute_set
while the drs_utils code has changed in moving
drs_get_rodc_partial_attribute_set() out of the class.
Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
2016-10-28 05:05:28 +03:00
if " systemFlags " in r :
system_flags = r [ " systemFlags " ] [ 0 ]
if ( int ( system_flags ) & ( samba . dsdb . DS_FLAG_ATTR_NOT_REPLICATED |
samba . dsdb . DS_FLAG_ATTR_IS_CONSTRUCTED ) ) :
continue
if " searchFlags " in r :
search_flags = r [ " searchFlags " ] [ 0 ]
if ( int ( search_flags ) & samba . dsdb . SEARCH_FLAG_RODC_ATTRIBUTE ) :
continue
attid = samdb . get_attid_from_lDAPDisplayName ( ldap_display_name )
attids . append ( int ( attid ) )
# the attids do need to be sorted, or windows doesn't return
# all the attributes we need
attids . sort ( )
partial_attribute_set . attids = attids
partial_attribute_set . num_attids = len ( attids )
return partial_attribute_set
2018-10-12 03:54:34 +03:00
def drs_copy_highwater_mark ( hwm , new_hwm ) :
"""
Copies the highwater mark by value , rather than by object reference . ( This
avoids lingering talloc references to old GetNCChanges reply messages ) .
"""
hwm . tmp_highest_usn = new_hwm . tmp_highest_usn
hwm . reserved_usn = new_hwm . reserved_usn
hwm . highest_usn = new_hwm . highest_usn
2011-12-08 23:23:57 +04:00
class drs_Replicate ( object ) :
2010-08-26 08:35:30 +04:00
''' DRS replication calls '''
2013-09-19 01:27:26 +04:00
def __init__ ( self , binding_string , lp , creds , samdb , invocation_id ) :
2010-08-26 08:35:30 +04:00
self . drs = drsuapi . drsuapi ( binding_string , lp , creds )
2018-11-05 06:54:05 +03:00
( self . drs_handle , self . supports_ext ) = drs_DsBind ( self . drs )
2010-08-26 08:35:30 +04:00
self . net = Net ( creds = creds , lp = lp )
self . samdb = samdb
2013-09-19 01:27:26 +04:00
if not isinstance ( invocation_id , misc . GUID ) :
raise RuntimeError ( " Must supply GUID for invocation_id " )
if invocation_id == misc . GUID ( " 00000000-0000-0000-0000-000000000000 " ) :
raise RuntimeError ( " Must not set GUID 00000000-0000-0000-0000-000000000000 as invocation_id " )
self . replication_state = self . net . replicate_init ( self . samdb , lp , self . drs , invocation_id )
2018-06-13 05:09:06 +03:00
self . more_flags = 0
2010-08-26 08:35:30 +04:00
2017-06-16 03:54:32 +03:00
def _should_retry_with_get_tgt ( self , error_code , req ) :
# If the error indicates we fail to resolve a target object for a
# linked attribute, then we should retry the request with GET_TGT
# (if we support it and haven't already tried that)
2018-11-05 07:01:55 +03:00
supports_ext = self . supports_ext
2017-06-16 03:54:32 +03:00
2022-04-28 11:33:07 +03:00
return ( error_code == werror . WERR_DS_DRA_RECYCLED_TARGET and
2018-11-05 07:01:55 +03:00
supports_ext & DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V10 and
( req . more_flags & drsuapi . DRSUAPI_DRS_GET_TGT ) == 0 )
2017-06-16 03:54:32 +03:00
2022-09-15 08:10:24 +03:00
@staticmethod
def _should_calculate_missing_anc_locally ( error_code , req ) :
# If the error indicates we fail to resolve the parent object
# for a new object, then we assume we are replicating from a
# buggy server (Samba 4.5 and earlier) that doesn't really
# understand how to implement GET_ANC
return ( ( error_code == werror . WERR_DS_DRA_MISSING_PARENT ) and
( req . replica_flags & drsuapi . DRSUAPI_DRS_GET_ANC ) != 0 )
def _calculate_missing_anc_locally ( self , ctr ) :
self . guids_seen = set ( )
# walk objects in ctr, add to guid_seen as we see them
# note if an object doesn't have a parent
object_to_check = ctr . first_object
while True :
if object_to_check is None :
break
self . guids_seen . add ( str ( object_to_check . object . identifier . guid ) )
if object_to_check . parent_object_guid is not None \
and object_to_check . parent_object_guid \
!= misc . GUID ( " 00000000-0000-0000-0000-000000000000 " ) \
and str ( object_to_check . parent_object_guid ) not in self . guids_seen :
obj_dn = ldb . Dn ( self . samdb , object_to_check . object . identifier . dn )
parent_dn = obj_dn . parent ( )
print ( f " Object { parent_dn } with "
f " GUID { object_to_check . parent_object_guid } "
" was not sent by the server in this chunk " )
object_to_check = object_to_check . next_object
2018-06-06 01:04:29 +03:00
def process_chunk ( self , level , ctr , schema , req_level , req , first_chunk ) :
''' Processes a single chunk of received replication data '''
# pass the replication into the py_net.c python bindings for processing
self . net . replicate_chunk ( self . replication_state , level , ctr ,
schema = schema , req_level = req_level , req = req )
2010-08-26 08:35:30 +04:00
def replicate ( self , dn , source_dsa_invocation_id , destination_dsa_guid ,
2010-11-07 05:53:13 +03:00
schema = False , exop = drsuapi . DRSUAPI_EXOP_NONE , rodc = False ,
2017-06-16 03:54:32 +03:00
replica_flags = None , full_sync = True , sync_forced = False , more_flags = 0 ) :
2010-08-26 08:35:30 +04:00
''' replicate a single DN '''
# setup for a GetNCChanges call
2018-11-05 06:54:05 +03:00
if self . supports_ext & DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V10 :
2017-06-16 03:54:32 +03:00
req = drsuapi . DsGetNCChangesRequest10 ( )
2018-06-13 05:09:06 +03:00
req . more_flags = ( more_flags | self . more_flags )
2017-06-16 03:54:32 +03:00
req_level = 10
else :
req_level = 8
req = drsuapi . DsGetNCChangesRequest8 ( )
2010-08-26 08:35:30 +04:00
2017-06-16 03:54:32 +03:00
req . destination_dsa_guid = destination_dsa_guid
req . source_dsa_invocation_id = source_dsa_invocation_id
req . naming_context = drsuapi . DsReplicaObjectIdentifier ( )
req . naming_context . dn = dn
2017-01-27 00:40:19 +03:00
2017-06-07 07:56:18 +03:00
# Default to a full replication if we don't find an upToDatenessVector
2017-02-17 08:22:19 +03:00
udv = None
2017-06-07 07:56:18 +03:00
hwm = drsuapi . DsReplicaHighWaterMark ( )
hwm . tmp_highest_usn = 0
hwm . reserved_usn = 0
hwm . highest_usn = 0
2017-02-17 08:22:19 +03:00
if not full_sync :
res = self . samdb . search ( base = dn , scope = ldb . SCOPE_BASE ,
attrs = [ " repsFrom " ] )
if " repsFrom " in res [ 0 ] :
for reps_from_packed in res [ 0 ] [ " repsFrom " ] :
reps_from_obj = ndr_unpack ( drsblobs . repsFromToBlob , reps_from_packed )
if reps_from_obj . ctr . source_dsa_invocation_id == source_dsa_invocation_id :
hwm = reps_from_obj . ctr . highwatermark
udv = drsuapi . DsReplicaCursorCtrEx ( )
udv . version = 1
udv . reserved1 = 0
udv . reserved2 = 0
cursors_v1 = [ ]
cursors_v2 = dsdb . _dsdb_load_udv_v2 ( self . samdb ,
self . samdb . get_default_basedn ( ) )
for cursor_v2 in cursors_v2 :
cursor_v1 = drsuapi . DsReplicaCursor ( )
cursor_v1 . source_dsa_invocation_id = cursor_v2 . source_dsa_invocation_id
cursor_v1 . highest_usn = cursor_v2 . highest_usn
cursors_v1 . append ( cursor_v1 )
udv . cursors = cursors_v1
udv . count = len ( cursors_v1 )
2017-06-16 03:54:32 +03:00
req . highwatermark = hwm
req . uptodateness_vector = udv
2017-01-27 00:18:21 +03:00
2010-11-07 05:53:13 +03:00
if replica_flags is not None :
2017-06-16 03:54:32 +03:00
req . replica_flags = replica_flags
2010-11-07 05:53:13 +03:00
elif exop == drsuapi . DRSUAPI_EXOP_REPL_SECRET :
2017-06-16 03:54:32 +03:00
req . replica_flags = 0
2010-08-26 08:35:30 +04:00
else :
2017-06-16 03:54:32 +03:00
req . replica_flags = ( drsuapi . DRSUAPI_DRS_INIT_SYNC |
drsuapi . DRSUAPI_DRS_PER_SYNC |
drsuapi . DRSUAPI_DRS_GET_ANC |
drsuapi . DRSUAPI_DRS_NEVER_SYNCED |
drsuapi . DRSUAPI_DRS_GET_ALL_GROUP_MEMBERSHIP )
2010-11-05 06:08:49 +03:00
if rodc :
2017-06-16 03:54:32 +03:00
req . replica_flags | = (
drsuapi . DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING )
2010-11-07 05:53:13 +03:00
else :
2017-06-16 03:54:32 +03:00
req . replica_flags | = drsuapi . DRSUAPI_DRS_WRIT_REP
2017-05-29 08:06:55 +03:00
if sync_forced :
2017-06-16 03:54:32 +03:00
req . replica_flags | = drsuapi . DRSUAPI_DRS_SYNC_FORCED
2017-05-29 08:06:55 +03:00
2017-06-16 03:54:32 +03:00
req . max_object_count = 402
req . max_ndr_size = 402116
req . extended_op = exop
req . fsmo_info = 0
req . partial_attribute_set = None
req . partial_attribute_set_ex = None
req . mapping_ctr . num_mappings = 0
req . mapping_ctr . mappings = None
2010-08-26 08:35:30 +04:00
2010-11-05 06:08:49 +03:00
if not schema and rodc :
2017-06-16 03:54:32 +03:00
req . partial_attribute_set = drs_get_rodc_partial_attribute_set ( self . samdb )
2010-08-26 08:35:30 +04:00
2018-11-05 06:54:05 +03:00
if not self . supports_ext & DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V8 :
2010-11-07 05:55:20 +03:00
req_level = 5
req5 = drsuapi . DsGetNCChangesRequest5 ( )
for a in dir ( req5 ) :
if a [ 0 ] != ' _ ' :
2017-06-16 03:54:32 +03:00
setattr ( req5 , a , getattr ( req , a ) )
2010-11-07 05:55:20 +03:00
req = req5
2017-01-27 00:18:21 +03:00
num_objects = 0
num_links = 0
2018-06-06 01:04:29 +03:00
first_chunk = True
2010-08-26 08:35:30 +04:00
while True :
2010-11-07 05:55:20 +03:00
( level , ctr ) = self . drs . DsGetNCChanges ( self . drs_handle , req_level , req )
2012-09-27 20:30:47 +04:00
if ctr . first_object is None and ctr . object_count != 0 :
2010-11-08 02:14:50 +03:00
raise RuntimeError ( " DsGetNCChanges: NULL first_object with object_count= %u " % ( ctr . object_count ) )
2017-06-16 03:54:32 +03:00
try :
2018-06-06 01:04:29 +03:00
self . process_chunk ( level , ctr , schema , req_level , req , first_chunk )
2017-06-16 03:54:32 +03:00
except WERRORError as e :
# Check if retrying with the GET_TGT flag set might resolve this error
2018-05-28 18:22:25 +03:00
if self . _should_retry_with_get_tgt ( e . args [ 0 ] , req ) :
2017-06-16 03:54:32 +03:00
print ( " Missing target object - retrying with DRS_GET_TGT " )
req . more_flags | = drsuapi . DRSUAPI_DRS_GET_TGT
2018-06-06 01:04:29 +03:00
# try sending the request again (this has the side-effect
# of causing the DC to restart the replication from scratch)
first_chunk = True
2017-06-16 03:54:32 +03:00
continue
2022-09-15 08:10:24 +03:00
if self . _should_calculate_missing_anc_locally ( e . args [ 0 ] ,
req ) :
print ( " Missing parent object - calculating missing objects locally " )
self . _calculate_missing_anc_locally ( ctr )
raise e
2017-01-27 00:18:21 +03:00
2018-06-06 01:04:29 +03:00
first_chunk = False
2017-01-27 00:18:21 +03:00
num_objects + = ctr . object_count
# Cope with servers that do not return level 6, so do not return any links
try :
num_links + = ctr . linked_attributes_count
except AttributeError :
pass
2010-08-26 08:35:30 +04:00
if ctr . more_data == 0 :
break
2018-10-12 03:54:34 +03:00
# update the request's HWM so we get the next chunk
drs_copy_highwater_mark ( req . highwatermark , ctr . new_highwatermark )
2017-01-27 00:18:21 +03:00
return ( num_objects , num_links )
2018-06-06 01:04:29 +03:00
# Handles the special case of creating a new clone of a DB, while also renaming
# the entire DB's objects on the way through
class drs_ReplicateRenamer ( drs_Replicate ) :
''' Uses DRS replication to rename the entire DB '''
def __init__ ( self , binding_string , lp , creds , samdb , invocation_id ,
old_base_dn , new_base_dn ) :
super ( drs_ReplicateRenamer , self ) . __init__ ( binding_string , lp , creds ,
samdb , invocation_id )
self . old_base_dn = old_base_dn
self . new_base_dn = new_base_dn
2018-06-13 05:09:06 +03:00
# because we're renaming the DNs, we know we're going to have trouble
# resolving link targets. Normally we'd get to the end of replication
# only to find we need to retry the whole replication with the GET_TGT
# flag set. Always setting the GET_TGT flag avoids this extra work.
self . more_flags = drsuapi . DRSUAPI_DRS_GET_TGT
2018-06-06 01:04:29 +03:00
def rename_dn ( self , dn_str ) :
''' Uses string substitution to replace the base DN '''
return re . sub ( ' %s $ ' % self . old_base_dn , self . new_base_dn , dn_str )
def update_name_attr ( self , base_obj ) :
''' Updates the ' name ' attribute for the base DN object '''
for attr in base_obj . attribute_ctr . attributes :
if attr . attid == DRSUAPI_ATTID_name :
base_dn = ldb . Dn ( self . samdb , base_obj . identifier . dn )
new_name = base_dn . get_rdn_value ( )
attr . value_ctr . values [ 0 ] . blob = new_name . encode ( ' utf-16-le ' )
def rename_top_level_object ( self , first_obj ) :
''' Renames the first/top-level object in a partition '''
old_dn = first_obj . identifier . dn
first_obj . identifier . dn = self . rename_dn ( first_obj . identifier . dn )
print ( " Renaming partition %s --> %s " % ( old_dn ,
first_obj . identifier . dn ) )
# we also need to fix up the 'name' attribute for the base DN,
# otherwise the RDNs won't match
if first_obj . identifier . dn == self . new_base_dn :
self . update_name_attr ( first_obj )
def process_chunk ( self , level , ctr , schema , req_level , req , first_chunk ) :
''' Processes a single chunk of received replication data '''
# we need to rename the NC in every chunk - this gets used in searches
# when applying the chunk
if ctr . naming_context :
ctr . naming_context . dn = self . rename_dn ( ctr . naming_context . dn )
# rename the first object in each partition. This will cause every
2023-06-06 14:31:52 +03:00
# subsequent object in the partition to be renamed as a side-effect
2018-06-06 01:04:29 +03:00
if first_chunk and ctr . object_count != 0 :
self . rename_top_level_object ( ctr . first_object . object )
# then do the normal repl processing to apply this chunk to our DB
super ( drs_ReplicateRenamer , self ) . process_chunk ( level , ctr , schema ,
req_level , req ,
first_chunk )