summaryrefslogtreecommitdiff
path: root/python/samba
diff options
context:
space:
mode:
authorAndrew Bartlett <abartlet@samba.org>2022-09-15 17:10:24 +1200
committerJule Anger <janger@samba.org>2022-10-07 08:48:17 +0000
commitbac9532f0a98ad54d4ad00f94bcbf13c797f823d (patch)
tree3b8571418540c5f639ac5b5b84f81e665693e38b /python/samba
parent79283760616bdd1ae811f8c407d601dfc48f019e (diff)
downloadsamba-bac9532f0a98ad54d4ad00f94bcbf13c797f823d.tar.gz
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 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> (cherry picked from commit bff2bc9c7d69ec2fbe9339c2353a0a846182f1ea)
Diffstat (limited to 'python/samba')
-rw-r--r--python/samba/drs_utils.py47
-rw-r--r--python/samba/join.py54
2 files changed, 90 insertions, 11 deletions
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