1
0
mirror of https://github.com/samba-team/samba.git synced 2025-07-27 07:42:04 +03:00

drs_utils: Add GET_TGT support to 'samba-tool drs replicate --local'

Update drs_Replicate.replicate() so it handles being passed the GET_TGT
flag (more_flags). To do this, we need to always use a v10 GetNCChanges
request (v8 and v10 are essentially the same except for the more_flags).

If the replicate_chunk() call into the C bindings throws an error, check
to see whether the error could be fixed by setting the GET_TGT flag, and
re-send the request if so.

Unfortunately because WERR_DS_DRA_RECYCLED_TARGET isn't documented with
the other AD error codes, I've left it hardcoded for now (Microsoft
should be fixing up their Docs).

Signed-off-by: Tim Beale <timbeale@catalyst.net.nz>
Reviewed-by: Garming Sam <garming@samba.org>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
BUG: https://bugzilla.samba.org/show_bug.cgi?id=12972
This commit is contained in:
Tim Beale
2017-06-16 12:54:32 +12:00
committed by Andrew Bartlett
parent f87332eb35
commit f812c29d40

View File

@ -21,6 +21,8 @@ from samba.dcerpc import drsuapi, misc, drsblobs
from samba.net import Net
from samba.ndr import ndr_unpack
from samba import dsdb
from samba import werror
from samba import WERRORError
import samba, ldb
@ -198,18 +200,37 @@ class drs_Replicate(object):
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)
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)
# TODO fix up the below line when we next update werror_err_table.txt
# and pull in the new error-code
# return (error_code == werror.WERR_DS_DRA_RECYCLED_TARGET and
return (error_code == 0x21bf and
(req.more_flags & drsuapi.DRSUAPI_DRS_GET_TGT) == 0 and
self.supported_extensions & drsuapi.DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V10)
def replicate(self, dn, source_dsa_invocation_id, destination_dsa_guid,
schema=False, exop=drsuapi.DRSUAPI_EXOP_NONE, rodc=False,
replica_flags=None, full_sync=True, sync_forced=False):
replica_flags=None, full_sync=True, sync_forced=False, more_flags=0):
'''replicate a single DN'''
# setup for a GetNCChanges call
req8 = drsuapi.DsGetNCChangesRequest8()
if self.supported_extensions & drsuapi.DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V10:
req = drsuapi.DsGetNCChangesRequest10()
req.more_flags = more_flags
req_level = 10
else:
req_level = 8
req = drsuapi.DsGetNCChangesRequest8()
req8.destination_dsa_guid = destination_dsa_guid
req8.source_dsa_invocation_id = source_dsa_invocation_id
req8.naming_context = drsuapi.DsReplicaObjectIdentifier()
req8.naming_context.dn = dn
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
# Default to a full replication if we don't find an upToDatenessVector
udv = None
@ -244,49 +265,46 @@ class drs_Replicate(object):
udv.cursors = cursors_v1
udv.count = len(cursors_v1)
req8.highwatermark = hwm
req8.uptodateness_vector = udv
req.highwatermark = hwm
req.uptodateness_vector = udv
if replica_flags is not None:
req8.replica_flags = replica_flags
req.replica_flags = replica_flags
elif exop == drsuapi.DRSUAPI_EXOP_REPL_SECRET:
req8.replica_flags = 0
req.replica_flags = 0
else:
req8.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)
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)
if rodc:
req8.replica_flags |= (
drsuapi.DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING)
req.replica_flags |= (
drsuapi.DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING)
else:
req8.replica_flags |= drsuapi.DRSUAPI_DRS_WRIT_REP
req.replica_flags |= drsuapi.DRSUAPI_DRS_WRIT_REP
if sync_forced:
req8.replica_flags |= drsuapi.DRSUAPI_DRS_SYNC_FORCED
req.replica_flags |= drsuapi.DRSUAPI_DRS_SYNC_FORCED
req8.max_object_count = 402
req8.max_ndr_size = 402116
req8.extended_op = exop
req8.fsmo_info = 0
req8.partial_attribute_set = None
req8.partial_attribute_set_ex = None
req8.mapping_ctr.num_mappings = 0
req8.mapping_ctr.mappings = None
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
if not schema and rodc:
req8.partial_attribute_set = drs_get_rodc_partial_attribute_set(self.samdb)
req.partial_attribute_set = drs_get_rodc_partial_attribute_set(self.samdb)
if self.supported_extensions & drsuapi.DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V8:
req_level = 8
req = req8
else:
if not self.supported_extensions & drsuapi.DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V8:
req_level = 5
req5 = drsuapi.DsGetNCChangesRequest5()
for a in dir(req5):
if a[0] != '_':
setattr(req5, a, getattr(req8, a))
setattr(req5, a, getattr(req, a))
req = req5
num_objects = 0
@ -295,8 +313,21 @@ class drs_Replicate(object):
(level, ctr) = self.drs.DsGetNCChanges(self.drs_handle, req_level, req)
if ctr.first_object is None and ctr.object_count != 0:
raise RuntimeError("DsGetNCChanges: NULL first_object with object_count=%u" % (ctr.object_count))
self.net.replicate_chunk(self.replication_state, level, ctr,
schema=schema, req_level=req_level, req=req)
try:
self.net.replicate_chunk(self.replication_state, level, ctr,
schema=schema, req_level=req_level, req=req)
except WERRORError as e:
# Check if retrying with the GET_TGT flag set might resolve this error
if self._should_retry_with_get_tgt(e[0], req):
print("Missing target object - retrying with DRS_GET_TGT")
req.more_flags |= drsuapi.DRSUAPI_DRS_GET_TGT
# try sending the request again
continue
else:
raise e
num_objects += ctr.object_count