diff options
-rw-r--r-- | source4/scripting/python/samba/join.py | 150 | ||||
-rw-r--r-- | source4/scripting/python/samba/provision/__init__.py | 137 |
2 files changed, 154 insertions, 133 deletions
diff --git a/source4/scripting/python/samba/join.py b/source4/scripting/python/samba/join.py index ccb9f06f28..195dfc2312 100644 --- a/source4/scripting/python/samba/join.py +++ b/source4/scripting/python/samba/join.py @@ -304,40 +304,47 @@ class dc_join(object): r.value_ctr = 1 - def DsAddEntry(ctx, rec): + def DsAddEntry(ctx, recs): '''add a record via the DRSUAPI DsAddEntry call''' if ctx.drsuapi is None: ctx.drsuapi_connect() if ctx.tmp_samdb is None: ctx.create_tmp_samdb() - id = drsuapi.DsReplicaObjectIdentifier() - id.dn = rec['dn'] - - attrs = [] - for a in rec: - if a == 'dn': - continue - if not isinstance(rec[a], list): - v = [rec[a]] - else: - v = rec[a] - rattr = ctx.tmp_samdb.dsdb_DsReplicaAttribute(ctx.tmp_samdb, a, v) - attrs.append(rattr) - - attribute_ctr = drsuapi.DsReplicaAttributeCtr() - attribute_ctr.num_attributes = len(attrs) - attribute_ctr.attributes = attrs - - object = drsuapi.DsReplicaObject() - object.identifier = id - object.attribute_ctr = attribute_ctr - - first_object = drsuapi.DsReplicaObjectListItem() - first_object.object = object + objects = [] + for rec in recs: + id = drsuapi.DsReplicaObjectIdentifier() + id.dn = rec['dn'] + + attrs = [] + for a in rec: + if a == 'dn': + continue + if not isinstance(rec[a], list): + v = [rec[a]] + else: + v = rec[a] + rattr = ctx.tmp_samdb.dsdb_DsReplicaAttribute(ctx.tmp_samdb, a, v) + attrs.append(rattr) + + attribute_ctr = drsuapi.DsReplicaAttributeCtr() + attribute_ctr.num_attributes = len(attrs) + attribute_ctr.attributes = attrs + + object = drsuapi.DsReplicaObject() + object.identifier = id + object.attribute_ctr = attribute_ctr + + list_object = drsuapi.DsReplicaObjectListItem() + list_object.object = object + objects.append(list_object) req2 = drsuapi.DsAddEntryRequest2() - req2.first_object = first_object + req2.first_object = objects[0] + prev = req2.first_object + for o in objects[1:]: + prev.next_object = o + prev = o (level, ctr) = ctx.drsuapi.DsAddEntry(ctx.drsuapi_handle, 2, req2) if ctr.err_ver != 1: @@ -349,6 +356,7 @@ class dc_join(object): if ctr.err_data.dir_err != drsuapi.DRSUAPI_DIRERR_OK: print("DsAddEntry failed with dir_err %u" % ctr.err_data.dir_err) raise RuntimeError("DsAddEntry failed") + return ctr.objects def join_add_ntdsdsa(ctx): @@ -361,17 +369,12 @@ class dc_join(object): "systemFlags" : str(samba.dsdb.SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE), "dMDLocation" : ctx.schema_dn} - if ctx.subdomain: - # the local subdomain NC doesn't exist at this time - # so we have to add the base_dn NC later - nc_list = [ ctx.config_dn, ctx.schema_dn ] - else: - nc_list = [ ctx.base_dn, ctx.config_dn, ctx.schema_dn ] + nc_list = [ ctx.base_dn, ctx.config_dn, ctx.schema_dn ] if ctx.behavior_version >= samba.dsdb.DS_DOMAIN_FUNCTION_2003: rec["msDS-Behavior-Version"] = str(ctx.behavior_version) - if ctx.behavior_version >= samba.dsdb.DS_DOMAIN_FUNCTION_2003 and not ctx.subdomain: + if ctx.behavior_version >= samba.dsdb.DS_DOMAIN_FUNCTION_2003: rec["msDS-HasDomainNCs"] = ctx.base_dn if ctx.RODC: @@ -386,32 +389,13 @@ class dc_join(object): rec["msDS-HasMasterNCs"] = nc_list rec["options"] = "1" rec["invocationId"] = ndr_pack(ctx.invocation_id) - if ctx.subdomain: - ctx.samdb.add(rec, ['relax:0']) - else: - ctx.DsAddEntry(rec) + ctx.DsAddEntry([rec]) # find the GUID of our NTDS DN res = ctx.samdb.search(base=ctx.ntds_dn, scope=ldb.SCOPE_BASE, attrs=["objectGUID"]) ctx.ntds_guid = misc.GUID(ctx.samdb.schema_format_value("objectGUID", res[0]["objectGUID"][0])) - def join_modify_ntdsdsa(ctx): - '''modify the ntdsdsa object to add local partitions''' - print "Modifying %s using system privileges" % ctx.ntds_dn - - # this works around the Enterprise Admins ACL on the NTDSDSA object - system_session_info = system_session() - ctx.samdb.set_session_info(system_session_info) - - m = ldb.Message() - m.dn = ldb.Dn(ctx.samdb, ctx.ntds_dn) - m["HasMasterNCs"] = ldb.MessageElement(ctx.base_dn, ldb.FLAG_MOD_ADD, "HasMasterNCs") - if ctx.behavior_version >= samba.dsdb.DS_DOMAIN_FUNCTION_2003: - m["msDS-HasDomainNCs"] = ldb.MessageElement(ctx.base_dn, ldb.FLAG_MOD_ADD, "msDS-HasDomainNCs") - m["msDS-HasMasterNCs"] = ldb.MessageElement(ctx.base_dn, ldb.FLAG_MOD_ADD, "msDS-HasMasterNCs") - ctx.samdb.modify(m, controls=['relax:0']) - def join_add_objects(ctx): '''add the various objects needed for the join''' if ctx.acct_dn: @@ -440,9 +424,11 @@ class dc_join(object): rec = { "dn": ctx.server_dn, "objectclass" : "server", + # windows uses 50000000 decimal for systemFlags. A windows hex/decimal mixup bug? "systemFlags" : str(samba.dsdb.SYSTEM_FLAG_CONFIG_ALLOW_RENAME | samba.dsdb.SYSTEM_FLAG_CONFIG_ALLOW_LIMITED_MOVE | samba.dsdb.SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE), + # windows seems to add the dnsHostName later "dnsHostName" : ctx.dnshostname} if ctx.acct_dn: @@ -507,9 +493,6 @@ class dc_join(object): def join_add_objects2(ctx): '''add the various objects needed for the join, for subdomains post replication''' - if not ctx.subdomain: - return - print "Adding %s" % ctx.partition_dn # NOTE: windows sends a ntSecurityDescriptor here, we # let it default @@ -524,8 +507,47 @@ class dc_join(object): "systemFlags" : str(samba.dsdb.SYSTEM_FLAG_CR_NTDS_NC|samba.dsdb.SYSTEM_FLAG_CR_NTDS_DOMAIN)} if ctx.behavior_version >= samba.dsdb.DS_DOMAIN_FUNCTION_2003: rec["msDS-Behavior-Version"] = str(ctx.behavior_version) - ctx.DsAddEntry(rec) + rec2 = { + "dn" : ctx.ntds_dn, + "objectclass" : "nTDSDSA", + "systemFlags" : str(samba.dsdb.SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE), + "dMDLocation" : ctx.schema_dn} + + nc_list = [ ctx.base_dn, ctx.config_dn, ctx.schema_dn ] + + if ctx.behavior_version >= samba.dsdb.DS_DOMAIN_FUNCTION_2003: + rec2["msDS-Behavior-Version"] = str(ctx.behavior_version) + + if ctx.behavior_version >= samba.dsdb.DS_DOMAIN_FUNCTION_2003: + rec2["msDS-HasDomainNCs"] = ctx.base_dn + + rec2["objectCategory"] = "CN=NTDS-DSA,%s" % ctx.schema_dn + rec2["HasMasterNCs"] = nc_list + if ctx.behavior_version >= samba.dsdb.DS_DOMAIN_FUNCTION_2003: + rec2["msDS-HasMasterNCs"] = nc_list + rec2["options"] = "1" + rec2["invocationId"] = ndr_pack(ctx.invocation_id) + + objects = ctx.DsAddEntry([rec, rec2]) + if len(objects) != 2: + raise DCJoinException("Expected 2 objects from DsAddEntry") + + ctx.ntds_guid = objects[1].guid + + print("Replicating partition DN") + ctx.repl.replicate(ctx.partition_dn, + misc.GUID("00000000-0000-0000-0000-000000000000"), + ctx.ntds_guid, + exop=drsuapi.DRSUAPI_EXOP_REPL_OBJ, + replica_flags=drsuapi.DRSUAPI_DRS_WRIT_REP) + + print("Replicating NTDS DN") + ctx.repl.replicate(ctx.ntds_dn, + misc.GUID("00000000-0000-0000-0000-000000000000"), + ctx.ntds_guid, + exop=drsuapi.DRSUAPI_EXOP_REPL_OBJ, + replica_flags=drsuapi.DRSUAPI_DRS_WRIT_REP) def join_provision(ctx): '''provision the local SAM''' @@ -565,14 +587,11 @@ class dc_join(object): ctx.local_samdb = ctx.samdb print("Finding domain GUID from ncName") - res = ctx.samdb.search(base=ctx.partition_dn, scope=ldb.SCOPE_BASE, attrs=['ncName'], - controls=["extended_dn:1:1"]) + res = ctx.local_samdb.search(base=ctx.partition_dn, scope=ldb.SCOPE_BASE, attrs=['ncName'], + controls=["extended_dn:1:1"]) domguid = str(misc.GUID(ldb.Dn(ctx.samdb, res[0]['ncName'][0]).get_extended_component('GUID'))) print("Got domain GUID %s" % domguid) - - ctx.join_add_ntdsdsa() - print("Calling own domain provision") logger = logging.getLogger("provision") @@ -597,6 +616,7 @@ class dc_join(object): try: source_dsa_invocation_id = misc.GUID(ctx.samdb.get_invocation_id()) if ctx.ntds_guid is None: + print("Using DS_BIND_GUID_W2K3") destination_dsa_guid = misc.GUID(drsuapi.DRSUAPI_DS_BIND_GUID_W2K3) else: destination_dsa_guid = ctx.ntds_guid @@ -634,6 +654,9 @@ class dc_join(object): repl.replicate(ctx.new_krbtgt_dn, source_dsa_invocation_id, destination_dsa_guid, exop=drsuapi.DRSUAPI_EXOP_REPL_SECRET, rodc=True) + ctx.repl = repl + ctx.source_dsa_invocation_id = source_dsa_invocation_id + ctx.destination_dsa_guid = destination_dsa_guid print "Committing SAM database" except: @@ -787,12 +810,11 @@ class dc_join(object): try: ctx.join_add_objects() ctx.join_provision() - ctx.join_add_objects2() ctx.join_replicate() if ctx.subdomain: + ctx.join_add_objects2() ctx.join_provision_own_domain() ctx.join_setup_trusts() - ctx.join_modify_ntdsdsa() ctx.join_finalise() except Exception: print "Join failed - cleaning up" diff --git a/source4/scripting/python/samba/provision/__init__.py b/source4/scripting/python/samba/provision/__init__.py index 0b39167df4..a3633ab128 100644 --- a/source4/scripting/python/samba/provision/__init__.py +++ b/source4/scripting/python/samba/provision/__init__.py @@ -995,11 +995,11 @@ def setup_secretsdb(paths, session_info, backend_credentials, lp): "LDAPADMINREALM": backend_credentials.get_realm(), "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password()) }) - - return secrets_ldb except Exception: secrets_ldb.transaction_cancel() raise + return secrets_ldb + def setup_privileges(path, session_info, lp): @@ -1597,10 +1597,6 @@ def provision_fill(samdb, secrets_ldb, logger, names, paths, setsysvolacl(samdb, paths.netlogon, paths.sysvol, paths.wheel_gid, domainsid, names.dnsdomain, names.domaindn, lp) - logger.info("Setting up sam.ldb rootDSE marking as synchronized") - setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"), - { 'NTDSGUID' : names.ntdsguid }) - secretsdb_self_join(secrets_ldb, domain=names.domain, realm=names.realm, dnsdomain=names.dnsdomain, netbiosname=names.netbiosname, domainsid=domainsid, @@ -1624,69 +1620,72 @@ def provision_fill(samdb, secrets_ldb, logger, names, paths, # It might be that this attribute does not exist in this schema raise - if serverrole == "domain controller": - secretsdb_setup_dns(secrets_ldb, names, - paths.private_dir, realm=names.realm, - dnsdomain=names.dnsdomain, - dns_keytab_path=paths.dns_keytab, dnspass=dnspass) - - # Default DNS backend is BIND9 using txt files for zone information - if not dns_backend: - dns_backend = "BIND9" - - setup_ad_dns(samdb, names, logger, hostip=hostip, hostip6=hostip6, - dns_backend=dns_backend, os_level=dom_for_fun_level) - - domainguid = samdb.searchone(basedn=samdb.get_default_basedn(), - attribute="objectGUID") - assert isinstance(domainguid, str) - - create_dns_dir(logger, paths) - - # Only make a zone file on the first DC, it should be - # replicated with DNS replication - if dns_backend == "BIND9": - create_zone_file(lp, logger, paths, targetdir, - dnsdomain=names.dnsdomain, hostip=hostip, hostip6=hostip6, - hostname=names.hostname, realm=names.realm, - domainguid=domainguid, ntdsguid=names.ntdsguid) - - create_named_conf(paths, realm=names.realm, - dnsdomain=names.dnsdomain, dns_backend=dns_backend) - - create_named_txt(paths.namedtxt, - realm=names.realm, dnsdomain=names.dnsdomain, - dnsname = "%s.%s" % (names.hostname, names.dnsdomain), - private_dir=paths.private_dir, - keytab_name=paths.dns_keytab) - logger.info("See %s for an example configuration include file for BIND", paths.namedconf) - logger.info("and %s for further documentation required for secure DNS " - "updates", paths.namedtxt) - - lastProvisionUSNs = get_last_provision_usn(samdb) - maxUSN = get_max_usn(samdb, str(names.rootdn)) - if lastProvisionUSNs is not None: - update_provision_usn(samdb, 0, maxUSN, invocationid, 1) - else: - set_provision_usn(samdb, 0, maxUSN, invocationid) - - # fix any dangling GUIDs from the provision - logger.info("Fixing provision GUIDs") - chk = dbcheck(samdb, samdb_schema=samdb, verbose=False, fix=True, yes=True, quiet=True) - samdb.transaction_start() - # a small number of GUIDs are missing because of ordering issues in the - # provision code - for schema_obj in ['CN=Domain', 'CN=Organizational-Person', 'CN=Contact', 'CN=inetOrgPerson']: - chk.check_database(DN="%s,%s" % (schema_obj, names.schemadn), - scope=ldb.SCOPE_BASE, attrs=['defaultObjectCategory']) - chk.check_database(DN="CN=IP Security,CN=System,%s" % names.domaindn, - scope=ldb.SCOPE_ONELEVEL, - attrs=['ipsecOwnersReference', - 'ipsecFilterReference', - 'ipsecISAKMPReference', - 'ipsecNegotiationPolicyReference', - 'ipsecNFAReference']) - samdb.transaction_commit() + secretsdb_setup_dns(secrets_ldb, names, + paths.private_dir, realm=names.realm, + dnsdomain=names.dnsdomain, + dns_keytab_path=paths.dns_keytab, dnspass=dnspass) + + # Default DNS backend is BIND9 using txt files for zone information + if not dns_backend: + dns_backend = "BIND9" + + setup_ad_dns(samdb, names, logger, hostip=hostip, hostip6=hostip6, + dns_backend=dns_backend, os_level=dom_for_fun_level) + + domainguid = samdb.searchone(basedn=samdb.get_default_basedn(), + attribute="objectGUID") + assert isinstance(domainguid, str) + + create_dns_dir(logger, paths) + + # Only make a zone file on the first DC, it should be + # replicated with DNS replication + if dns_backend == "BIND9": + create_zone_file(lp, logger, paths, targetdir, + dnsdomain=names.dnsdomain, hostip=hostip, hostip6=hostip6, + hostname=names.hostname, realm=names.realm, + domainguid=domainguid, ntdsguid=names.ntdsguid) + + create_named_conf(paths, realm=names.realm, + dnsdomain=names.dnsdomain, dns_backend=dns_backend) + + create_named_txt(paths.namedtxt, + realm=names.realm, dnsdomain=names.dnsdomain, + dnsname = "%s.%s" % (names.hostname, names.dnsdomain), + private_dir=paths.private_dir, + keytab_name=paths.dns_keytab) + logger.info("See %s for an example configuration include file for BIND", paths.namedconf) + logger.info("and %s for further documentation required for secure DNS " + "updates", paths.namedtxt) + + lastProvisionUSNs = get_last_provision_usn(samdb) + maxUSN = get_max_usn(samdb, str(names.rootdn)) + if lastProvisionUSNs is not None: + update_provision_usn(samdb, 0, maxUSN, invocationid, 1) + else: + set_provision_usn(samdb, 0, maxUSN, invocationid) + + logger.info("Setting up sam.ldb rootDSE marking as synchronized") + setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"), + { 'NTDSGUID' : names.ntdsguid }) + + # fix any dangling GUIDs from the provision + logger.info("Fixing provision GUIDs") + chk = dbcheck(samdb, samdb_schema=samdb, verbose=False, fix=True, yes=True, quiet=True) + samdb.transaction_start() + # a small number of GUIDs are missing because of ordering issues in the + # provision code + for schema_obj in ['CN=Domain', 'CN=Organizational-Person', 'CN=Contact', 'CN=inetOrgPerson']: + chk.check_database(DN="%s,%s" % (schema_obj, names.schemadn), + scope=ldb.SCOPE_BASE, attrs=['defaultObjectCategory']) + chk.check_database(DN="CN=IP Security,CN=System,%s" % names.domaindn, + scope=ldb.SCOPE_ONELEVEL, + attrs=['ipsecOwnersReference', + 'ipsecFilterReference', + 'ipsecISAKMPReference', + 'ipsecNegotiationPolicyReference', + 'ipsecNFAReference']) + samdb.transaction_commit() def provision(logger, session_info, credentials, smbconf=None, |