sys.path.insert(0, "bin/python")
-import samba
+import samba, ldb
import samba.getopt as options
from samba.dcerpc import drsuapi, misc
from samba.samdb import SamDB
from samba.auth import system_session
+from samba.ndr import ndr_unpack
def do_DsBind(drs):
'''make a DsBind call, returning the binding handle'''
(info, handle) = drs.DsBind(misc.GUID(drsuapi.DRSUAPI_DS_BIND_GUID), bind_info)
return handle
+
+def drs_get_rodc_partial_attribute_set(samdb):
+ '''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 set SPECIAL_SECRET_PROCESSING
+ # to zero them out
+ schema_dn = samdb.get_schema_basedn()
+ res = 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 = 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
+
+
########### main code ###########
if __name__ == "__main__":
parser = OptionParser("getncchanges [options] server")
parser.add_option("", "--dn", dest="dn", help="DN to replicate",)
parser.add_option("", "--exop", dest="exop", help="extended operation",)
+ parser.add_option("", "--pas", dest="use_pas", action='store_true', default=False,
+ help="send partial attribute set (for RODC)")
+ parser.add_option("", "--nb-iter", type='int', help="Number of getncchange iterations")
+ parser.add_option("", "--dest-dsa", type='str', help="destination DSA GUID")
+ parser.add_option("", "--rodc", action='store_true', default=False,
+ help='use RODC replica flags')
+ parser.add_option("", "--partial-rw", action='store_true', default=False,
+ help='use RW partial replica flags, not be confused with --pas')
+ parser.add_option("", "--replica-flags", type='int',
+ default=drsuapi.DRSUAPI_DRS_INIT_SYNC |
+ drsuapi.DRSUAPI_DRS_PER_SYNC |
+ drsuapi.DRSUAPI_DRS_WRIT_REP |
+ drsuapi.DRSUAPI_DRS_GET_ANC |
+ drsuapi.DRSUAPI_DRS_NEVER_SYNCED,
+ help='replica flags')
(opts, args) = parser.parse_args()
+ if opts.rodc:
+ opts.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_SPECIAL_SECRET_PROCESSING |\
+ drsuapi.DRSUAPI_DRS_GET_ALL_GROUP_MEMBERSHIP
+
+ if opts.partial_rw:
+ opts.replica_flags = drsuapi.DRSUAPI_DRS_INIT_SYNC |\
+ drsuapi.DRSUAPI_DRS_PER_SYNC |\
+ drsuapi.DRSUAPI_DRS_GET_ANC |\
+ drsuapi.DRSUAPI_DRS_NEVER_SYNCED
lp = sambaopts.get_loadparm()
creds = credopts.get_credentials(lp)
if creds.is_anonymous():
parser.error("You must supply credentials")
+ if opts.partial_rw and opts.rodc:
+ parser.error("Can't specify --partial-rw and --rodc")
+
server = args[0]
binding_str = "ncacn_ip_tcp:%s[seal,print]" % server
session_info=system_session(),
credentials=creds, lp=lp)
+ if opts.use_pas:
+ local_samdb = SamDB(url=None, session_info=system_session(),
+ credentials=creds, lp=lp)
+
if opts.dn is None:
opts.dn = str(samdb.get_default_basedn())
else:
exop = int(opts.exop)
+ dest_dsa = opts.dest_dsa
+ if not dest_dsa:
+ print "no dest_dsa specified trying to figure out from ldap"
+ msgs = samdb.search(controls=["search_options:1:2"],
+ expression='(objectclass=ntdsdsa)')
+ if len(msgs) == 1:
+ dest_dsa = str(ndr_unpack(misc.GUID, msgs[0]["invocationId"][0]))
+ print "Found this dsa: %s" % dest_dsa
+ else:
+ # TODO fixme
+ pass
+ if not dest_dsa:
+ print "Unable to find the dest_dsa automatically please specify it"
+ import sys
+ sys.exit(1)
+
null_guid = misc.GUID()
- req8.destination_dsa_guid = misc.GUID("9c637462-5b8c-4467-aef2-bdb1f57bc4ef")
+ req8.destination_dsa_guid = misc.GUID(dest_dsa)
req8.source_dsa_invocation_id = misc.GUID(samdb.get_invocation_id())
req8.naming_context = drsuapi.DsReplicaObjectIdentifier()
req8.naming_context.dn = opts.dn.decode("utf-8")
req8.highwatermark.reserved_usn = 0
req8.highwatermark.highest_usn = 0
req8.uptodateness_vector = None
- req8.replica_flags = 0
- req8.replica_flags |= (drsuapi.DRSUAPI_DRS_INIT_SYNC |
- drsuapi.DRSUAPI_DRS_PER_SYNC |
- drsuapi.DRSUAPI_DRS_GET_ANC |
- drsuapi.DRSUAPI_DRS_NEVER_SYNCED)
+ req8.replica_flags = opts.replica_flags
req8.max_object_count = 402
req8.max_ndr_size = 402116
req8.extended_op = exop
req8.fsmo_info = 0
- req8.partial_attribute_set = None
+ if opts.use_pas:
+ req8.partial_attribute_set = drs_get_rodc_partial_attribute_set(local_samdb)
+ else:
+ req8.partial_attribute_set = None
req8.partial_attribute_set_ex = None
req8.mapping_ctr.num_mappings = 0
req8.mapping_ctr.mappings = None
- drs.DsGetNCChanges(drs_handle, 8, req8)
+ nb_iter = 0
+ while True:
+ (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8)
+ nb_iter += 1
+ if ctr.more_data == 0 or opts.nb_iter == nb_iter:
+ break
+ req8.highwatermark = ctr.new_highwatermark