From: Andrew Bartlett Date: Thu, 15 Sep 2022 05:10:24 +0000 (+1200) Subject: python-drs: Add client-side debug and fallback for GET_ANC X-Git-Tag: talloc-2.4.0~773 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=bff2bc9c7d69ec2fbe9339c2353a0a846182f1ea;p=thirdparty%2Fsamba.git 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 Reviewed-by: Douglas Bagnall --- diff --git a/python/samba/drs_utils.py b/python/samba/drs_utils.py index a71da6eedd3..6399e5f7fbc 100644 --- a/python/samba/drs_utils.py +++ b/python/samba/drs_utils.py @@ -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 diff --git a/python/samba/join.py b/python/samba/join.py index 97561323f21..650bb5a08ae 100644 --- a/python/samba/join.py +++ b/python/samba/join.py @@ -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 diff --git a/selftest/knownfail.d/samba-4.5-emulation b/selftest/knownfail.d/samba-4.5-emulation index 37baa41822c..1fc79361e40 100644 --- a/selftest/knownfail.d/samba-4.5-emulation +++ b/selftest/knownfail.d/samba-4.5-emulation @@ -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\) \ No newline at end of file