summaryrefslogtreecommitdiff
path: root/python/samba/drs_utils.py
diff options
context:
space:
mode:
authorTim Beale <timbeale@catalyst.net.nz>2017-06-16 12:54:32 +1200
committerAndrew Bartlett <abartlet@samba.org>2017-08-18 06:07:12 +0200
commitf812c29d4000ef35fa5d7f6007606ca53c5ed37a (patch)
treef7b6e217b9d62512325640bfe16bf96e7ab671f0 /python/samba/drs_utils.py
parentf87332eb35638cc38f83c580d4623ab978088601 (diff)
downloadsamba-f812c29d4000ef35fa5d7f6007606ca53c5ed37a.tar.gz
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
Diffstat (limited to 'python/samba/drs_utils.py')
-rw-r--r--python/samba/drs_utils.py101
1 files changed, 66 insertions, 35 deletions
diff --git a/python/samba/drs_utils.py b/python/samba/drs_utils.py
index b9ed0597d4b..bed88178b74 100644
--- a/python/samba/drs_utils.py
+++ b/python/samba/drs_utils.py
@@ -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