1
0
mirror of https://github.com/samba-team/samba.git synced 2025-01-21 18:04:06 +03:00

python-drs: Add client-side debug and fallback for GET_ANC

Samba 4.5 and earlier will fail to do GET_ANC correctly and will not
replicate non-critical parents of objects with isCriticalSystemObject=TRUE
when DRSUAPI_DRS_CRITICAL_ONLY is set.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=15189

Signed-off-by: Andrew Bartlett <abartlet@samba.org>
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
This commit is contained in:
Andrew Bartlett 2022-09-15 17:10:24 +12:00
parent 483c48f52d
commit bff2bc9c7d
3 changed files with 90 additions and 12 deletions

View File

@ -204,6 +204,44 @@ class drs_Replicate(object):
supports_ext & DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V10 and
(req.more_flags & drsuapi.DRSUAPI_DRS_GET_TGT) == 0)
@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
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
@ -326,8 +364,13 @@ class drs_Replicate(object):
# of causing the DC to restart the replication from scratch)
first_chunk = True
continue
else:
raise e
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
first_chunk = False
num_objects += ctr.object_count

View File

@ -968,17 +968,53 @@ class DCJoinContext(object):
destination_dsa_guid, rodc=ctx.RODC,
replica_flags=ctx.replica_flags)
if not ctx.subdomain:
# Replicate first the critical object for the basedn
if not ctx.domain_replica_flags & drsuapi.DRSUAPI_DRS_CRITICAL_ONLY:
print("Replicating critical objects from the base DN of the domain")
ctx.domain_replica_flags |= drsuapi.DRSUAPI_DRS_CRITICAL_ONLY
# Replicate first the critical objects for the basedn
# We do this to match Windows. The default case is to
# do a critical objects replication, then a second
# with all objects.
print("Replicating critical objects from the base DN of the domain")
try:
repl.replicate(ctx.base_dn, source_dsa_invocation_id,
destination_dsa_guid, rodc=ctx.RODC,
replica_flags=ctx.domain_replica_flags)
ctx.domain_replica_flags ^= drsuapi.DRSUAPI_DRS_CRITICAL_ONLY
repl.replicate(ctx.base_dn, source_dsa_invocation_id,
destination_dsa_guid, rodc=ctx.RODC,
replica_flags=ctx.domain_replica_flags)
replica_flags=ctx.domain_replica_flags | drsuapi.DRSUAPI_DRS_CRITICAL_ONLY)
except WERRORError as e:
if e.args[0] == werror.WERR_DS_DRA_MISSING_PARENT:
ctx.logger.warning("First pass of replication with "
"DRSUAPI_DRS_CRITICAL_ONLY "
"not possible due to a missing parent object. "
"This is typical of a Samba "
"4.5 or earlier server. "
"We will replicate the all objects instead.")
else:
raise
# Now replicate all the objects in the domain (unless
# we were run with --critical-only).
#
# Doing the replication of users as a second pass
# matches more closely the Windows behaviour, which is
# actually to do this on first startup.
#
# Use --critical-only if you want that (but you don't
# really, it is better to see any errors here).
if not ctx.domain_replica_flags & drsuapi.DRSUAPI_DRS_CRITICAL_ONLY:
try:
repl.replicate(ctx.base_dn, source_dsa_invocation_id,
destination_dsa_guid, rodc=ctx.RODC,
replica_flags=ctx.domain_replica_flags)
except WERRORError as e:
if e.args[0] == werror.WERR_DS_DRA_MISSING_PARENT and \
ctx.domain_replica_flags & drsuapi.DRSUAPI_DRS_CRITICAL_ONLY:
ctx.logger.warning("Replication with DRSUAPI_DRS_CRITICAL_ONLY "
"failed due to a missing parent object. "
"This may be a Samba 4.5 or earlier server "
"and is not compatible with --critical-only")
raise
print("Done with always replicated NC (base, config, schema)")
# At this point we should already have an entry in the ForestDNS

View File

@ -2,4 +2,3 @@
samba4.drs.getnc_exop.python\(chgdcpass\).getnc_exop.DrsReplicaSyncTestCase.test_FSMONotOwner\(chgdcpass\)
# This fails because GET_ANC is now poorly implemented (matching Samba 4.5)
^samba4.drs.getnc_exop.python\(chgdcpass\).getnc_exop.DrsReplicaSyncTestCase.test_link_utdv_hwm\(chgdcpass\)
^samba4.drs.samba_tool_drs_critical.python\(chgdcpass\).samba_tool_drs_critical.SambaToolDrsTests.test_samba_tool_drs_clone_dc_critical_object_chain\(chgdcpass:local\)