class drs_Replicate(object): '''DRS replication calls''' def __init__(self, binding_string, lp, creds, samdb, invocation_id): self.drs = drsuapi.drsuapi(binding_string, lp, creds) (self.drs_handle, self.supported_extensions) = drs_DsBind(self.drs) self.net = Net(creds=creds, lp=lp) self.samdb = samdb if not isinstance(invocation_id, misc.GUID): raise RuntimeError("Must supply GUID for invocation_id") if invocation_id == misc.GUID("00000000-0000-0000-0000-000000000000"): 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 drs_get_rodc_partial_attribute_set(self): '''get a list of attributes for RODC replication''' partial_attribute_set = drsuapi.DsPartialAttributeSet() partial_attribute_set.version = 1 attids = [] # the exact list of attids we send is quite critical. Note that # we do ask for the secret attributes, but set SPECIAL_SECRET_PROCESSING # to zero them out schema_dn = self.samdb.get_schema_basedn() res = self.samdb.search( base=schema_dn, scope=ldb.SCOPE_SUBTREE, expression="objectClass=attributeSchema", attrs=["lDAPDisplayName", "systemFlags", "searchFlags"]) for r in res: ldap_display_name = r["lDAPDisplayName"][0] if "systemFlags" in r: system_flags = r["systemFlags"][0] if (int(system_flags) & (samba.dsdb.DS_FLAG_ATTR_NOT_REPLICATED | samba.dsdb.DS_FLAG_ATTR_IS_CONSTRUCTED)): continue if "searchFlags" in r: search_flags = r["searchFlags"][0] if (int(search_flags) & samba.dsdb.SEARCH_FLAG_RODC_ATTRIBUTE): continue attid = self.samdb.get_attid_from_lDAPDisplayName( ldap_display_name) attids.append(int(attid)) # the attids do need to be sorted, or windows doesn't return # all the attributes we need attids.sort() partial_attribute_set.attids = attids partial_attribute_set.num_attids = len(attids) return partial_attribute_set def replicate(self, dn, source_dsa_invocation_id, destination_dsa_guid, schema=False, exop=drsuapi.DRSUAPI_EXOP_NONE, rodc=False, replica_flags=None): '''replicate a single DN''' # setup for a GetNCChanges call req8 = 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 req8.highwatermark = drsuapi.DsReplicaHighWaterMark() req8.highwatermark.tmp_highest_usn = 0 req8.highwatermark.reserved_usn = 0 req8.highwatermark.highest_usn = 0 req8.uptodateness_vector = None if replica_flags is not None: req8.replica_flags = replica_flags elif exop == drsuapi.DRSUAPI_EXOP_REPL_SECRET: req8.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) if rodc: req8.replica_flags |= ( drsuapi.DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING) else: req8.replica_flags |= drsuapi.DRSUAPI_DRS_WRIT_REP 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 if not schema and rodc: req8.partial_attribute_set = self.drs_get_rodc_partial_attribute_set( ) if self.supported_extensions & drsuapi.DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V8: req_level = 8 req = req8 else: req_level = 5 req5 = drsuapi.DsGetNCChangesRequest5() for a in dir(req5): if a[0] != '_': setattr(req5, a, getattr(req8, a)) req = req5 while True: (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) if ctr.more_data == 0: break req.highwatermark = ctr.new_highwatermark
def test_net_replicate_chunk_1(self): lp, creds, server = self.get_lp_et_al() ctr = drsuapi.DsGetNCChangesCtr6() net = Net(creds, lp, server=server) net.replicate_chunk(42, 1, ctr)
class drs_Replicate(object): '''DRS replication calls''' def __init__(self, binding_string, lp, creds, samdb, invocation_id): self.drs = drsuapi.drsuapi(binding_string, lp, creds) (self.drs_handle, self.supported_extensions) = drs_DsBind(self.drs) self.net = Net(creds=creds, lp=lp) self.samdb = samdb if not isinstance(invocation_id, misc.GUID): raise RuntimeError("Must supply GUID for invocation_id") if invocation_id == misc.GUID("00000000-0000-0000-0000-000000000000"): 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 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): '''replicate a single DN''' # setup for a GetNCChanges call req8 = 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 # Default to a full replication if we don't find an upToDatenessVector udv = None hwm = drsuapi.DsReplicaHighWaterMark() hwm.tmp_highest_usn = 0 hwm.reserved_usn = 0 hwm.highest_usn = 0 if not full_sync: res = self.samdb.search(base=dn, scope=ldb.SCOPE_BASE, attrs=["repsFrom"]) if "repsFrom" in res[0]: for reps_from_packed in res[0]["repsFrom"]: reps_from_obj = ndr_unpack(drsblobs.repsFromToBlob, reps_from_packed) if reps_from_obj.ctr.source_dsa_invocation_id == source_dsa_invocation_id: hwm = reps_from_obj.ctr.highwatermark udv = drsuapi.DsReplicaCursorCtrEx() udv.version = 1 udv.reserved1 = 0 udv.reserved2 = 0 cursors_v1 = [] cursors_v2 = dsdb._dsdb_load_udv_v2( self.samdb, self.samdb.get_default_basedn()) for cursor_v2 in cursors_v2: cursor_v1 = drsuapi.DsReplicaCursor() cursor_v1.source_dsa_invocation_id = cursor_v2.source_dsa_invocation_id cursor_v1.highest_usn = cursor_v2.highest_usn cursors_v1.append(cursor_v1) udv.cursors = cursors_v1 udv.count = len(cursors_v1) req8.highwatermark = hwm req8.uptodateness_vector = udv if replica_flags is not None: req8.replica_flags = replica_flags elif exop == drsuapi.DRSUAPI_EXOP_REPL_SECRET: req8.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) if rodc: req8.replica_flags |= ( drsuapi.DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING) else: req8.replica_flags |= drsuapi.DRSUAPI_DRS_WRIT_REP if sync_forced: req8.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 if not schema and rodc: req8.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: req_level = 5 req5 = drsuapi.DsGetNCChangesRequest5() for a in dir(req5): if a[0] != '_': setattr(req5, a, getattr(req8, a)) req = req5 num_objects = 0 num_links = 0 while True: (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) num_objects += ctr.object_count # Cope with servers that do not return level 6, so do not return any links try: num_links += ctr.linked_attributes_count except AttributeError: pass if ctr.more_data == 0: break req.highwatermark = ctr.new_highwatermark return (num_objects, num_links)
class drs_Replicate(object): """DRS replication calls""" def __init__(self, binding_string, lp, creds, samdb, invocation_id): self.drs = drsuapi.drsuapi(binding_string, lp, creds) (self.drs_handle, self.supported_extensions) = drs_DsBind(self.drs) self.net = Net(creds=creds, lp=lp) self.samdb = samdb if not isinstance(invocation_id, misc.GUID): raise RuntimeError("Must supply GUID for invocation_id") if invocation_id == misc.GUID("00000000-0000-0000-0000-000000000000"): 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 drs_get_rodc_partial_attribute_set(self): """get a list of attributes for RODC replication""" partial_attribute_set = drsuapi.DsPartialAttributeSet() partial_attribute_set.version = 1 attids = [] # the exact list of attids we send is quite critical. Note that # we do ask for the secret attributes, but set SPECIAL_SECRET_PROCESSING # to zero them out schema_dn = self.samdb.get_schema_basedn() res = self.samdb.search( base=schema_dn, scope=ldb.SCOPE_SUBTREE, expression="objectClass=attributeSchema", attrs=["lDAPDisplayName", "systemFlags", "searchFlags"], ) for r in res: ldap_display_name = r["lDAPDisplayName"][0] if "systemFlags" in r: system_flags = r["systemFlags"][0] if int(system_flags) & ( samba.dsdb.DS_FLAG_ATTR_NOT_REPLICATED | samba.dsdb.DS_FLAG_ATTR_IS_CONSTRUCTED ): continue if "searchFlags" in r: search_flags = r["searchFlags"][0] if int(search_flags) & samba.dsdb.SEARCH_FLAG_RODC_ATTRIBUTE: continue attid = self.samdb.get_attid_from_lDAPDisplayName(ldap_display_name) attids.append(int(attid)) # the attids do need to be sorted, or windows doesn't return # all the attributes we need attids.sort() partial_attribute_set.attids = attids partial_attribute_set.num_attids = len(attids) return partial_attribute_set def replicate( self, dn, source_dsa_invocation_id, destination_dsa_guid, schema=False, exop=drsuapi.DRSUAPI_EXOP_NONE, rodc=False, replica_flags=None, ): """replicate a single DN""" # setup for a GetNCChanges call req8 = 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 req8.highwatermark = drsuapi.DsReplicaHighWaterMark() req8.highwatermark.tmp_highest_usn = 0 req8.highwatermark.reserved_usn = 0 req8.highwatermark.highest_usn = 0 req8.uptodateness_vector = None if replica_flags is not None: req8.replica_flags = replica_flags elif exop == drsuapi.DRSUAPI_EXOP_REPL_SECRET: req8.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 ) if rodc: req8.replica_flags |= drsuapi.DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING else: req8.replica_flags |= drsuapi.DRSUAPI_DRS_WRIT_REP 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 if not schema and rodc: req8.partial_attribute_set = self.drs_get_rodc_partial_attribute_set() if self.supported_extensions & drsuapi.DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V8: req_level = 8 req = req8 else: req_level = 5 req5 = drsuapi.DsGetNCChangesRequest5() for a in dir(req5): if a[0] != "_": setattr(req5, a, getattr(req8, a)) req = req5 while True: (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={0:d}".format((ctr.object_count)) ) self.net.replicate_chunk(self.replication_state, level, ctr, schema=schema, req_level=req_level, req=req) if ctr.more_data == 0: break req.highwatermark = ctr.new_highwatermark
class drs_Replicate(object): '''DRS replication calls''' def __init__(self, binding_string, lp, creds, samdb, invocation_id): self.drs = drsuapi.drsuapi(binding_string, lp, creds) (self.drs_handle, self.supported_extensions) = drs_DsBind(self.drs) self.net = Net(creds=creds, lp=lp) self.samdb = samdb if not isinstance(invocation_id, misc.GUID): raise RuntimeError("Must supply GUID for invocation_id") if invocation_id == misc.GUID("00000000-0000-0000-0000-000000000000"): 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 replicate(self, dn, source_dsa_invocation_id, destination_dsa_guid, schema=False, exop=drsuapi.DRSUAPI_EXOP_NONE, rodc=False, replica_flags=None, highwatermark=None, udv=None): '''replicate a single DN''' # setup for a GetNCChanges call req8 = 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 if highwatermark is not None: req8.highwatermark = highwatermark else: req8.highwatermark = drsuapi.DsReplicaHighWaterMark() req8.highwatermark.tmp_highest_usn = 0 req8.highwatermark.reserved_usn = 0 req8.highwatermark.highest_usn = 0 req8.uptodateness_vector = udv if replica_flags is not None: req8.replica_flags = replica_flags elif exop == drsuapi.DRSUAPI_EXOP_REPL_SECRET: req8.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) if rodc: req8.replica_flags |= ( drsuapi.DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING) else: req8.replica_flags |= drsuapi.DRSUAPI_DRS_WRIT_REP 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 if not schema and rodc: req8.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: req_level = 5 req5 = drsuapi.DsGetNCChangesRequest5() for a in dir(req5): if a[0] != '_': setattr(req5, a, getattr(req8, a)) req = req5 num_objects = 0 num_links = 0 while True: (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) num_objects += ctr.object_count # Cope with servers that do not return level 6, so do not return any links try: num_links += ctr.linked_attributes_count except AttributeError: pass if ctr.more_data == 0: break req.highwatermark = ctr.new_highwatermark return (num_objects, num_links)
class drs_Replicate(object): '''DRS replication calls''' def __init__(self, binding_string, lp, creds, samdb, invocation_id): self.drs = drsuapi.drsuapi(binding_string, lp, creds) (self.drs_handle, self.supported_extensions) = drs_DsBind(self.drs) self.net = Net(creds=creds, lp=lp) self.samdb = samdb if not isinstance(invocation_id, misc.GUID): raise RuntimeError("Must supply GUID for invocation_id") if invocation_id == misc.GUID("00000000-0000-0000-0000-000000000000"): 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 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): '''replicate a single DN''' # setup for a GetNCChanges call req8 = 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 udv = None if not full_sync: res = self.samdb.search(base=dn, scope=ldb.SCOPE_BASE, attrs=["repsFrom"]) if "repsFrom" in res[0]: for reps_from_packed in res[0]["repsFrom"]: reps_from_obj = ndr_unpack(drsblobs.repsFromToBlob, reps_from_packed) if reps_from_obj.ctr.source_dsa_invocation_id == source_dsa_invocation_id: hwm = reps_from_obj.ctr.highwatermark udv = drsuapi.DsReplicaCursorCtrEx() udv.version = 1 udv.reserved1 = 0 udv.reserved2 = 0 cursors_v1 = [] cursors_v2 = dsdb._dsdb_load_udv_v2(self.samdb, self.samdb.get_default_basedn()) for cursor_v2 in cursors_v2: cursor_v1 = drsuapi.DsReplicaCursor() cursor_v1.source_dsa_invocation_id = cursor_v2.source_dsa_invocation_id cursor_v1.highest_usn = cursor_v2.highest_usn cursors_v1.append(cursor_v1) udv.cursors = cursors_v1 udv.count = len(cursors_v1) # If we can't find an upToDateVector, or where told not to, replicate fully hwm = drsuapi.DsReplicaHighWaterMark() hwm.tmp_highest_usn = 0 hwm.reserved_usn = 0 hwm.highest_usn = 0 req8.highwatermark = hwm req8.uptodateness_vector = udv if replica_flags is not None: req8.replica_flags = replica_flags elif exop == drsuapi.DRSUAPI_EXOP_REPL_SECRET: req8.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) if rodc: req8.replica_flags |= ( drsuapi.DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING) else: req8.replica_flags |= drsuapi.DRSUAPI_DRS_WRIT_REP 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 if not schema and rodc: req8.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: req_level = 5 req5 = drsuapi.DsGetNCChangesRequest5() for a in dir(req5): if a[0] != '_': setattr(req5, a, getattr(req8, a)) req = req5 num_objects = 0 num_links = 0 while True: (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) num_objects += ctr.object_count # Cope with servers that do not return level 6, so do not return any links try: num_links += ctr.linked_attributes_count except AttributeError: pass if ctr.more_data == 0: break req.highwatermark = ctr.new_highwatermark return (num_objects, num_links)
class drs_Replicate(object): '''DRS replication calls''' def __init__(self, binding_string, lp, creds, samdb, invocation_id): self.drs = drsuapi.drsuapi(binding_string, lp, creds) (self.drs_handle, self.supported_extensions) = drs_DsBind(self.drs) self.net = Net(creds=creds, lp=lp) self.samdb = samdb if not isinstance(invocation_id, misc.GUID): raise RuntimeError("Must supply GUID for invocation_id") if invocation_id == misc.GUID("00000000-0000-0000-0000-000000000000"): 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) self.more_flags = 0 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 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 self.net.replicate_chunk(self.replication_state, level, ctr, schema=schema, req_level=req_level, req=req) 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, more_flags=0): '''replicate a single DN''' # setup for a GetNCChanges call if self.supported_extensions & drsuapi.DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V10: req = drsuapi.DsGetNCChangesRequest10() req.more_flags = (more_flags | self.more_flags) req_level = 10 else: req_level = 8 req = drsuapi.DsGetNCChangesRequest8() 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 hwm = drsuapi.DsReplicaHighWaterMark() hwm.tmp_highest_usn = 0 hwm.reserved_usn = 0 hwm.highest_usn = 0 if not full_sync: res = self.samdb.search(base=dn, scope=ldb.SCOPE_BASE, attrs=["repsFrom"]) if "repsFrom" in res[0]: for reps_from_packed in res[0]["repsFrom"]: reps_from_obj = ndr_unpack(drsblobs.repsFromToBlob, reps_from_packed) if reps_from_obj.ctr.source_dsa_invocation_id == source_dsa_invocation_id: hwm = reps_from_obj.ctr.highwatermark udv = drsuapi.DsReplicaCursorCtrEx() udv.version = 1 udv.reserved1 = 0 udv.reserved2 = 0 cursors_v1 = [] cursors_v2 = dsdb._dsdb_load_udv_v2( self.samdb, self.samdb.get_default_basedn()) for cursor_v2 in cursors_v2: cursor_v1 = drsuapi.DsReplicaCursor() cursor_v1.source_dsa_invocation_id = cursor_v2.source_dsa_invocation_id cursor_v1.highest_usn = cursor_v2.highest_usn cursors_v1.append(cursor_v1) udv.cursors = cursors_v1 udv.count = len(cursors_v1) req.highwatermark = hwm req.uptodateness_vector = udv if replica_flags is not None: req.replica_flags = replica_flags elif exop == drsuapi.DRSUAPI_EXOP_REPL_SECRET: req.replica_flags = 0 else: 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: req.replica_flags |= ( drsuapi.DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING) else: req.replica_flags |= drsuapi.DRSUAPI_DRS_WRIT_REP if sync_forced: req.replica_flags |= drsuapi.DRSUAPI_DRS_SYNC_FORCED 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: req.partial_attribute_set = drs_get_rodc_partial_attribute_set( self.samdb) 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(req, a)) req = req5 num_objects = 0 num_links = 0 first_chunk = True while True: (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)) try: self.process_chunk(level, ctr, schema, req_level, req, first_chunk) 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.args[0], req): print("Missing target object - retrying with DRS_GET_TGT") req.more_flags |= drsuapi.DRSUAPI_DRS_GET_TGT # try sending the request again (this has the side-effect # of causing the DC to restart the replication from scratch) first_chunk = True continue else: raise e first_chunk = False num_objects += ctr.object_count # Cope with servers that do not return level 6, so do not return any links try: num_links += ctr.linked_attributes_count except AttributeError: pass if ctr.more_data == 0: break # update the request's HWM so we get the next chunk drs_copy_highwater_mark(req.highwatermark, ctr.new_highwatermark) return (num_objects, num_links)
class drs_Replicate(object): '''DRS replication calls''' def __init__(self, binding_string, lp, creds, samdb, invocation_id): self.drs = drsuapi.drsuapi(binding_string, lp, creds) (self.drs_handle, self.supported_extensions) = drs_DsBind(self.drs) self.net = Net(creds=creds, lp=lp) self.samdb = samdb if not isinstance(invocation_id, misc.GUID): raise RuntimeError("Must supply GUID for invocation_id") if invocation_id == misc.GUID("00000000-0000-0000-0000-000000000000"): 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, more_flags=0): '''replicate a single DN''' # setup for a GetNCChanges call 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() 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 hwm = drsuapi.DsReplicaHighWaterMark() hwm.tmp_highest_usn = 0 hwm.reserved_usn = 0 hwm.highest_usn = 0 if not full_sync: res = self.samdb.search(base=dn, scope=ldb.SCOPE_BASE, attrs=["repsFrom"]) if "repsFrom" in res[0]: for reps_from_packed in res[0]["repsFrom"]: reps_from_obj = ndr_unpack(drsblobs.repsFromToBlob, reps_from_packed) if reps_from_obj.ctr.source_dsa_invocation_id == source_dsa_invocation_id: hwm = reps_from_obj.ctr.highwatermark udv = drsuapi.DsReplicaCursorCtrEx() udv.version = 1 udv.reserved1 = 0 udv.reserved2 = 0 cursors_v1 = [] cursors_v2 = dsdb._dsdb_load_udv_v2(self.samdb, self.samdb.get_default_basedn()) for cursor_v2 in cursors_v2: cursor_v1 = drsuapi.DsReplicaCursor() cursor_v1.source_dsa_invocation_id = cursor_v2.source_dsa_invocation_id cursor_v1.highest_usn = cursor_v2.highest_usn cursors_v1.append(cursor_v1) udv.cursors = cursors_v1 udv.count = len(cursors_v1) req.highwatermark = hwm req.uptodateness_vector = udv if replica_flags is not None: req.replica_flags = replica_flags elif exop == drsuapi.DRSUAPI_EXOP_REPL_SECRET: req.replica_flags = 0 else: 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: req.replica_flags |= ( drsuapi.DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING) else: req.replica_flags |= drsuapi.DRSUAPI_DRS_WRIT_REP if sync_forced: req.replica_flags |= drsuapi.DRSUAPI_DRS_SYNC_FORCED 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: req.partial_attribute_set = drs_get_rodc_partial_attribute_set(self.samdb) 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(req, a)) req = req5 num_objects = 0 num_links = 0 while True: (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)) 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 # Cope with servers that do not return level 6, so do not return any links try: num_links += ctr.linked_attributes_count except AttributeError: pass if ctr.more_data == 0: break req.highwatermark = ctr.new_highwatermark return (num_objects, num_links)