s4-provision: move zone file to dns subdirectory
[ira/wip.git] / source4 / scripting / python / samba / provision.py
index 86e7cec6ab3148833e0993095f69e3fd33518977..fb4e9b71f57d93f2d6e33ba06e84b4ad15ab5667 100644 (file)
 
 from base64 import b64encode
 import os
-import sys
 import pwd
 import grp
 import time
-import uuid, glue
+import uuid
 import socket
 import param
 import registry
-import samba
-import subprocess
-import ldb
+import urllib
+import shutil
 
+import ldb
 
-from auth import system_session, admin_session
-from samba import version, Ldb, substitute_var, valid_netbios_name, setup_file
-from samba import check_all_substituted, read_and_sub_file
-from samba import DS_DOMAIN_FUNCTION_2003, DS_DOMAIN_FUNCTION_2008, DS_DC_FUNCTION_2008
-from samba.samdb import SamDB
-from samba.idmap import IDmapDB
+from samba.auth import system_session, admin_session
+from samba import glue, version, Ldb, substitute_var, valid_netbios_name
+from samba import check_all_substituted, read_and_sub_file, setup_file
+from samba import DS_DOMAIN_FUNCTION_2003, DS_DC_FUNCTION_2008
 from samba.dcerpc import security
-from samba.ndr import ndr_pack
-import urllib
-from ldb import SCOPE_SUBTREE, SCOPE_ONELEVEL, SCOPE_BASE, LdbError
+from samba.dcerpc.misc import SEC_CHAN_BDC, SEC_CHAN_WKSTA
+from samba.idmap import IDmapDB
+from samba.ntacls import setntacl, dsacl2fsacl
+from samba.ndr import ndr_pack,ndr_unpack
+from samba.schema import Schema
 from ms_display_specifiers import read_ms_ldif
-from schema import Schema
-from provisionbackend import LDBBackend, ExistingBackend, FDSBackend, OpenLDAPBackend
-from signal import SIGTERM
-from dcerpc.misc import SEC_CHAN_BDC, SEC_CHAN_WKSTA
+from samba.provisionbackend import LDBBackend, ExistingBackend, FDSBackend, OpenLDAPBackend
+from provisionexceptions import ProvisioningError, InvalidNetbiosName
 
 __docformat__ = "restructuredText"
 
@@ -90,11 +87,11 @@ def get_config_descriptor(domain_sid):
            "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
            "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
            "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
-           "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;S-1-5-21-3191434175-1265308384-3577286990-498)" \
+           "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
            "S:(AU;SA;WPWOWD;;;WD)(AU;SA;CR;;;BA)(AU;SA;CR;;;DU)" \
            "(OU;SA;CR;45ec5156-db7e-47bb-b53f-dbeb2d03c40f;;WD)"
     sec = security.descriptor.from_sddl(sddl, domain_sid)
-    return b64encode(ndr_pack(sec))
+    return ndr_pack(sec)
 
 def get_domain_descriptor(domain_sid):
     sddl= "O:BAG:BAD:AI(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
@@ -107,7 +104,7 @@ def get_domain_descriptor(domain_sid):
     "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
     "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
     "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
-    "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;S-1-5-21-832762594-175224951-1765713900-498)" \
+    "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
     "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;DD)" \
     "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a86-0de6-11d0-a285-00aa003049e2;ED)" \
     "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a9c-0de6-11d0-a285-00aa003049e2;ED)" \
@@ -118,7 +115,7 @@ def get_domain_descriptor(domain_sid):
     "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
     "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
     "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
-    "(OA;;CR;e2a36dc9-ae17-47c3-b58b-be34c55ba633;;S-1-5-32-557)" \
+    "(OA;;CR;e2a36dc9-ae17-47c3-b58b-be34c55ba633;;IF)" \
     "(OA;;RP;c7407360-20bf-11d0-a768-00aa006e0529;;RU)" \
     "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;RU)" \
     "(OA;CIIO;RPLCLORC;;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
@@ -147,21 +144,12 @@ def get_domain_descriptor(domain_sid):
     "(OU;CISA;WP;f30e3bbf-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
     "(AU;SA;CR;;;DU)(AU;SA;CR;;;BA)(AU;SA;WPWOWD;;;WD)"
     sec = security.descriptor.from_sddl(sddl, domain_sid)
-    return b64encode(ndr_pack(sec))
+    return ndr_pack(sec)
 
 DEFAULTSITE = "Default-First-Site-Name"
 
 # Exception classes
 
-class ProvisioningError(Exception):
-    """A generic provision error."""
-
-class InvalidNetbiosName(Exception):
-    """A specified name was not a valid NetBIOS name."""
-    def __init__(self, name):
-        super(InvalidNetbiosName, self).__init__("The name '%r' is not a valid NetBIOS name" % name)
-
-
 class ProvisionPaths(object):
     def __init__(self):
         self.shareconf = None
@@ -183,15 +171,6 @@ class ProvisionPaths(object):
         self.slapdconf = None
         self.modulesconf = None
         self.memberofconf = None
-        self.fedoradsinf = None
-        self.fedoradspartitions = None
-        self.fedoradssasl = None
-        self.fedoradsdna = None
-        self.fedoradspam = None
-        self.fedoradsrefint = None
-        self.fedoradslinkedattributes = None
-        self.fedoradsindex = None
-        self.fedoradssamba = None
         self.olmmron = None
         self.olmmrserveridsconf = None
         self.olmmrsyncreplconf = None
@@ -316,7 +295,7 @@ def provision_paths_from_lp(lp, dnsdomain):
     paths.idmapdb = os.path.join(paths.private_dir, lp.get("idmap database") or "idmap.ldb")
     paths.secrets = os.path.join(paths.private_dir, lp.get("secrets database") or "secrets.ldb")
     paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
-    paths.dns = os.path.join(paths.private_dir, dnsdomain + ".zone")
+    paths.dns = os.path.join(paths.private_dir, "dns", dnsdomain + ".zone")
     paths.namedconf = os.path.join(paths.private_dir, "named.conf")
     paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
     paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
@@ -334,24 +313,6 @@ def provision_paths_from_lp(lp, dnsdomain):
                                      "modules.conf")
     paths.memberofconf = os.path.join(paths.ldapdir, 
                                       "memberof.conf")
-    paths.fedoradsinf = os.path.join(paths.ldapdir, 
-                                     "fedorads.inf")
-    paths.fedoradspartitions = os.path.join(paths.ldapdir, 
-                                            "fedorads-partitions.ldif")
-    paths.fedoradssasl = os.path.join(paths.ldapdir, 
-                                      "fedorads-sasl.ldif")
-    paths.fedoradsdna = os.path.join(paths.ldapdir, 
-                                     "fedorads-dna.ldif")
-    paths.fedoradspam = os.path.join(paths.ldapdir,
-                                     "fedorads-pam.ldif")
-    paths.fedoradsrefint = os.path.join(paths.ldapdir,
-                                        "fedorads-refint.ldif")
-    paths.fedoradslinkedattributes = os.path.join(paths.ldapdir,
-                                                  "fedorads-linked-attributes.ldif")
-    paths.fedoradsindex = os.path.join(paths.ldapdir,
-                                       "fedorads-index.ldif")
-    paths.fedoradssamba = os.path.join(paths.ldapdir, 
-                                       "fedorads-samba.ldif")
     paths.olmmrserveridsconf = os.path.join(paths.ldapdir, 
                                             "mmr_serverids.conf")
     paths.olmmrsyncreplconf = os.path.join(paths.ldapdir, 
@@ -394,12 +355,16 @@ def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
 
     if dnsdomain is None:
         dnsdomain = lp.get("realm")
-    assert dnsdomain is not None
+        if dnsdomain is None or dnsdomain == "":
+            raise ProvisioningError("guess_names: 'realm' not specified in supplied smb.conf!")
+
     dnsdomain = dnsdomain.lower()
 
     if serverrole is None:
         serverrole = lp.get("server role")
-    assert serverrole is not None
+        if serverrole is None:
+            raise ProvisioningError("guess_names: 'server role' not specified in supplied smb.conf!")
+
     serverrole = serverrole.lower()
 
     realm = dnsdomain.upper()
@@ -407,10 +372,14 @@ def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
     if lp.get("realm").upper() != realm:
         raise ProvisioningError("guess_names: Realm '%s' in smb.conf must match chosen realm '%s'!", lp.get("realm").upper(), realm)
 
+    if lp.get("server role").lower() != serverrole:
+        raise ProvisioningError("guess_names: server role '%s' in smb.conf must match chosen server role '%s'!", lp.get("server role").upper(), serverrole)
+
     if serverrole == "domain controller":
         if domain is None:
             domain = lp.get("workgroup")
-        assert domain is not None
+        if domain is None:
+            raise ProvisioningError("guess_names: 'workgroup' not specified in supplied smb.conf!")
         domain = domain.upper()
 
         if lp.get("workgroup").upper() != domain:
@@ -462,7 +431,7 @@ def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
     
 
 def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole, 
-                 targetdir, sid_generator):
+                 targetdir, sid_generator,eadb):
     """Create a new smb.conf file based on a couple of basic settings.
     """
     assert smbconf is not None
@@ -494,7 +463,11 @@ def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
     #Load non-existant file
     if os.path.exists(smbconf):
         default_lp.load(smbconf)
-    
+    if eadb:
+        posixeadb_line = "posix:eadb = " + os.path.abspath(os.path.join(os.path.join(targetdir, "private"),"eadb.tdb"))
+    else:
+        posixeadb_line = ""
+
     if targetdir is not None:
         privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
         lockdir_line = "lock dir = " + os.path.abspath(targetdir)
@@ -522,7 +495,8 @@ def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
             "SYSVOLPATH": sysvol,
             "SIDGENERATOR_LINE": sid_generator_line,
             "PRIVATEDIR_LINE": privatedir_line,
-            "LOCKDIR_LINE": lockdir_line
+            "LOCKDIR_LINE": lockdir_line,
+                       "POSIXEADB_LINE": posixeadb_line
             })
 
 
@@ -576,89 +550,25 @@ def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
     samdb = Ldb(url=samdb_path, session_info=session_info, 
                 lp=lp, options=["modules:"])
 
-    #Add modules to the list to activate them by default
-    #beware often order is important
-    #
-    # Some Known ordering constraints:
-    # - rootdse must be first, as it makes redirects from "" -> cn=rootdse
-    # - objectclass must be before password_hash, because password_hash checks
-    #   that the objectclass is of type person (filled in by objectclass
-    #   module when expanding the objectclass list)
-    # - partition must be last
-    # - each partition has its own module list then
-    modules_list = ["resolve_oids",
-                    "rootdse",
-                    "lazy_commit",
-                    "paged_results",
-                    "ranged_results",
-                    "anr",
-                    "server_sort",
-                    "asq",
-                    "extended_dn_store",
-                    "extended_dn_in",
-                    "rdn_name",
-                    "objectclass",
-                    "descriptor",
-                    "acl",
-                    "samldb",
-                    "password_hash",
-                    "operational",
-                    "kludge_acl", 
-                    "instancetype"]
-    tdb_modules_list = [
-                    "subtree_rename",
-                    "subtree_delete",
-                    "linked_attributes",
-                    "extended_dn_out_ldb"]
-    modules_list2 = ["show_deleted",
-                     "schema_load",
-                     "new_partition",
-                     "partition"]
-
     ldap_backend_line = "# No LDAP backend"
     if provision_backend.type is not "ldb":
         ldap_backend_line = "ldapBackend: %s" % provision_backend.ldapi_uri
-        
-        if provision_backend.ldap_backend_type == "fedora-ds":
-            backend_modules = ["nsuniqueid", "paged_searches"]
-            # We can handle linked attributes here, as we don't have directory-side subtree operations
-            tdb_modules_list = ["extended_dn_out_fds"]
-        elif provision_backend.ldap_backend_type == "openldap":
-            backend_modules = ["entryuuid", "paged_searches"]
-            # OpenLDAP handles subtree renames, so we don't want to do any of these things
-            tdb_modules_list = ["extended_dn_out_openldap"]
-
-    elif serverrole == "domain controller":
-        tdb_modules_list.insert(0, "repl_meta_data")
-        backend_modules = []
-    else:
-        backend_modules = ["objectguid"]
 
-    if tdb_modules_list is None:
-        tdb_modules_list_as_string = ""
-    else:
-        tdb_modules_list_as_string = ","+",".join(tdb_modules_list)
-        
     samdb.transaction_start()
     try:
         message("Setting up sam.ldb partitions and settings")
         setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
                 "SCHEMADN": ldb.Dn(schema.ldb, names.schemadn).get_casefold(), 
-                "SCHEMADN_MOD2": ",objectguid",
                 "CONFIGDN": ldb.Dn(schema.ldb, names.configdn).get_casefold(),
                 "DOMAINDN": ldb.Dn(schema.ldb, names.domaindn).get_casefold(),
-                "SCHEMADN_MOD": "schema_data",
-                "CONFIGDN_MOD": "naming_fsmo",
-                "DOMAINDN_MOD": "pdc_fsmo",
-                "MODULES_LIST": ",".join(modules_list),
-                "TDB_MODULES_LIST": tdb_modules_list_as_string,
-                "MODULES_LIST2": ",".join(modules_list2),
-                "BACKEND_MOD": ",".join(backend_modules),
                 "LDAP_BACKEND_LINE": ldap_backend_line,
         })
 
         
-        samdb.load_ldif_file_add(setup_path("provision_init.ldif"))
+        setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
+                "BACKEND_TYPE": provision_backend.type,
+                "SERVER_ROLE": serverrole
+                })
 
         message("Setting up sam.ldb rootDSE")
         setup_samdb_rootdse(samdb, setup_path, names)
@@ -671,7 +581,7 @@ def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
 
         
 def secretsdb_self_join(secretsdb, domain, 
-                        netbiosname, domainsid, machinepass, 
+                        netbiosname, machinepass, domainsid=None,
                         realm=None, dnsdomain=None,
                         keytab_path=None, 
                         key_version_number=1,
@@ -706,18 +616,19 @@ def secretsdb_self_join(secretsdb, domain,
     msg["secret"] = [machinepass]
     msg["samAccountName"] = ["%s$" % netbiosname]
     msg["secureChannelType"] = [str(secure_channel_type)]
-    msg["objectSid"] = [ndr_pack(domainsid)]
+    if domainsid is not None:
+        msg["objectSid"] = [ndr_pack(domainsid)]
     
     res = secretsdb.search(base="cn=Primary Domains", 
                            attrs=attrs, 
                            expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain))" % (domain, realm, str(domainsid))), 
-                           scope=SCOPE_ONELEVEL)
+                           scope=ldb.SCOPE_ONELEVEL)
     
     for del_msg in res:
       if del_msg.dn is not msg.dn:
         secretsdb.delete(del_msg.dn)
 
-    res = secretsdb.search(base=msg.dn, attrs=attrs, scope=SCOPE_BASE)
+    res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
 
     if len(res) == 1:
       msg["priorSecret"] = res[0]["secret"]
@@ -736,7 +647,8 @@ def secretsdb_self_join(secretsdb, domain,
       secretsdb.add(msg)
 
 
-def secretsdb_setup_dns(secretsdb, setup_path, realm, dnsdomain, 
+def secretsdb_setup_dns(secretsdb, setup_path, private_dir,
+                        realm, dnsdomain,
                         dns_keytab_path, dnspass):
     """Add DNS specific bits to a secrets database.
     
@@ -744,6 +656,11 @@ def secretsdb_setup_dns(secretsdb, setup_path, realm, dnsdomain,
     :param setup_path: Setup path function
     :param machinepass: Machine password
     """
+    try:
+        os.unlink(os.path.join(private_dir, dns_keytab_path))
+    except OSError:
+        pass
+
     setup_ldb(secretsdb, setup_path("secrets_dns.ldif"), { 
             "REALM": realm,
             "DNSDOMAIN": dnsdomain,
@@ -883,9 +800,9 @@ def setup_self_join(samdb, names,
               "DEFAULTSITE": names.sitename,
               "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
               "MACHINEPASS_B64": b64encode(machinepass),
-              "DNSPASS_B64": b64encode(dnspass),
               "REALM": names.realm,
               "DOMAIN": names.domain,
+              "DOMAINSID": str(domainsid),
               "DNSDOMAIN": names.dnsdomain,
               "SAMBA_VERSION_STRING": version,
               "NTDSGUID": ntdsguid_line,
@@ -901,7 +818,7 @@ def setup_self_join(samdb, names,
     # add the NTDSGUID based SPNs
     ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
     names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID",
-                                     expression="", scope=SCOPE_BASE)
+                                     expression="", scope=ldb.SCOPE_BASE)
     assert isinstance(names.ntdsguid, str)
 
     # Setup fSMORoleOwner entries to point at the newly created DC entry
@@ -914,10 +831,29 @@ def setup_self_join(samdb, names,
               "DEFAULTSITE": names.sitename,
               "SERVERDN": names.serverdn,
               "NETBIOSNAME": names.netbiosname,
-              "NTDSGUID": names.ntdsguid
+              "NTDSGUID": names.ntdsguid,
+              "DNSPASS_B64": b64encode(dnspass),
               })
 
 
+def setup_gpo(paths,names,samdb,policyguid,policyguid_dc,domainsid):
+    policy_path = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
+                               "{" + policyguid + "}")
+    os.makedirs(policy_path, 0755)
+    open(os.path.join(policy_path, "GPT.INI"), 'w').write(
+                      "[General]\r\nVersion=65543")
+    os.makedirs(os.path.join(policy_path, "MACHINE"), 0755)
+    os.makedirs(os.path.join(policy_path, "USER"), 0755)
+
+    policy_path_dc = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
+                                  "{" + policyguid_dc + "}")
+    os.makedirs(policy_path_dc, 0755)
+    open(os.path.join(policy_path_dc, "GPT.INI"), 'w').write(
+                      "[General]\r\nVersion=2")
+    os.makedirs(os.path.join(policy_path_dc, "MACHINE"), 0755)
+    os.makedirs(os.path.join(policy_path_dc, "USER"), 0755)
+
+
 def setup_samdb(path, setup_path, session_info, provision_backend, lp, 
                 names, message, 
                 domainsid, domainguid, policyguid, policyguid_dc,
@@ -937,7 +873,7 @@ def setup_samdb(path, setup_path, session_info, provision_backend, lp,
     if dom_for_fun_level is None:
         dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
     if dom_for_fun_level < DS_DOMAIN_FUNCTION_2003:
-        raise ProvisioningError("You want to run SAMBA 4 on a domain and forest function level lower than Windows 2003 (Native). This isn't supported!")
+        message("You want to run SAMBA 4 on a domain and forest function level lower than Windows 2003 (Native). This is not recommended")
 
     if dom_for_fun_level > domainControllerFunctionality:
         raise ProvisioningError("You want to run SAMBA 4 on a domain and forest function level which itself is higher than its actual DC function level (2008). This won't work!")
@@ -982,8 +918,7 @@ def setup_samdb(path, setup_path, session_info, provision_backend, lp,
         samdb.set_opaque_integer("domainControllerFunctionality", domainControllerFunctionality)
 
         samdb.set_domain_sid(str(domainsid))
-        if serverrole == "domain controller":
-            samdb.set_invocation_id(invocationid)
+        samdb.set_invocation_id(invocationid)
 
         message("Adding DomainDN: %s" % names.domaindn)
 
@@ -995,7 +930,7 @@ def setup_samdb(path, setup_path, session_info, provision_backend, lp,
         else:
             domainguid_line = ""
 
-        descr = get_domain_descriptor(domainsid)
+        descr = b64encode(get_domain_descriptor(domainsid))
         setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
                 "DOMAINDN": names.domaindn,
                 "DOMAINGUID": domainguid_line,
@@ -1018,16 +953,11 @@ def setup_samdb(path, setup_path, session_info, provision_backend, lp,
             })
 
         message("Adding configuration container")
-        descr = get_config_descriptor(domainsid);
+        descr = b64encode(get_config_descriptor(domainsid))
         setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
             "CONFIGDN": names.configdn, 
             "DESCRIPTOR": descr,
             })
-        message("Modifying configuration container")
-        setup_modify_ldif(samdb, setup_path("provision_configuration_basedn_modify.ldif"), {
-            "CONFIGDN": names.configdn, 
-            "SCHEMADN": names.schemadn,
-            })
 
         # The LDIF here was created when the Schema object was constructed
         message("Setting up sam.ldb schema")
@@ -1038,6 +968,14 @@ def setup_samdb(path, setup_path, session_info, provision_backend, lp,
         setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"), 
                        {"SCHEMADN": names.schemadn})
 
+        message("Reopening sam.ldb with new schema");
+        samdb.transaction_commit()
+        samdb = Ldb(session_info=admin_session_info,
+                    credentials=provision_backend.credentials, lp=lp)
+        samdb.connect(path)
+        samdb.transaction_start()
+        samdb.set_invocation_id(invocationid)
+
         message("Setting up sam.ldb configuration data")
         setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
             "CONFIGDN": names.configdn,
@@ -1080,6 +1018,12 @@ def setup_samdb(path, setup_path, session_info, provision_backend, lp,
             "POLICYGUID_DC": policyguid_dc
             })
 
+        setup_modify_ldif(samdb, setup_path("provision_basedn_references.ldif"), {
+                "DOMAINDN": names.domaindn})
+
+        setup_modify_ldif(samdb, setup_path("provision_configuration_references.ldif"), {
+                "CONFIGDN": names.configdn,
+                "SCHEMADN": names.schemadn})
         if fill == FILL_FULL:
             message("Setting up sam.ldb users and groups")
             setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
@@ -1090,21 +1034,20 @@ def setup_samdb(path, setup_path, session_info, provision_backend, lp,
                 "KRBTGTPASS_B64": b64encode(krbtgtpass),
                 })
 
-            if serverrole == "domain controller":
-                message("Setting up self join")
-                setup_self_join(samdb, names=names, invocationid=invocationid, 
-                                dnspass=dnspass,  
-                                machinepass=machinepass, 
-                                domainsid=domainsid, policyguid=policyguid,
-                                policyguid_dc=policyguid_dc,
-                                setup_path=setup_path,
-                                domainControllerFunctionality=domainControllerFunctionality,
-                                ntdsguid=ntdsguid)
-
-                ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
-                names.ntdsguid = samdb.searchone(basedn=ntds_dn,
-                  attribute="objectGUID", expression="", scope=SCOPE_BASE)
-                assert isinstance(names.ntdsguid, str)
+            message("Setting up self join")
+            setup_self_join(samdb, names=names, invocationid=invocationid,
+                            dnspass=dnspass,
+                            machinepass=machinepass,
+                            domainsid=domainsid, policyguid=policyguid,
+                            policyguid_dc=policyguid_dc,
+                            setup_path=setup_path,
+                            domainControllerFunctionality=domainControllerFunctionality,
+                            ntdsguid=ntdsguid)
+
+            ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
+            names.ntdsguid = samdb.searchone(basedn=ntds_dn,
+                attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
+            assert isinstance(names.ntdsguid, str)
 
     except:
         samdb.transaction_cancel()
@@ -1117,6 +1060,49 @@ def setup_samdb(path, setup_path, session_info, provision_backend, lp,
 FILL_FULL = "FULL"
 FILL_NT4SYNC = "NT4SYNC"
 FILL_DRS = "DRS"
+SYSVOL_ACL = "O:${DOMAINSID}-500G:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;S-1-5-32-549)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
+POLICIES_ACL = "O:${DOMAINSID}-500G:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;S-1-5-32-549)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)(A;OICI;0x001301bf;;;${DOMAINSID}-520)"
+
+def set_gpo_acl(path,acl,lp,domsid):
+       setntacl(lp,path,acl,domsid)
+       for root, dirs, files in os.walk(path, topdown=False):
+               for name in files:
+                       setntacl(lp,os.path.join(root, name),acl,domsid)
+               for name in dirs:
+                       setntacl(lp,os.path.join(root, name),acl,domsid)
+
+def setsysvolacl(samdb,names,netlogon,sysvol,gid,domainsid,lp):
+       canchown = 1
+       acl = SYSVOL_ACL.replace("${DOMAINSID}",str(domainsid))
+       try:
+               os.chown(sysvol,-1,gid)
+       except:
+               canchown = 0
+
+       setntacl(lp,sysvol,acl,str(domainsid))
+       for root, dirs, files in os.walk(sysvol, topdown=False):
+               for name in files:
+                       if canchown:
+                               os.chown(os.path.join(root, name),-1,gid)
+                       setntacl(lp,os.path.join(root, name),acl,str(domainsid))
+               for name in dirs:
+                       if canchown:
+                               os.chown(os.path.join(root, name),-1,gid)
+                       setntacl(lp,os.path.join(root, name),acl,str(domainsid))
+
+       # Set ACL for GPO
+       policy_path = os.path.join(sysvol, names.dnsdomain, "Policies")
+       acl = POLICIES_ACL.replace("${DOMAINSID}",str(domainsid))
+       set_gpo_acl(policy_path,dsacl2fsacl(acl,str(domainsid)),lp,str(domainsid))
+       res = samdb.search(base="CN=Policies,CN=System,%s"%(names.domaindn),
+                                               attrs=["cn","nTSecurityDescriptor"],
+                                               expression="", scope=ldb.SCOPE_ONELEVEL)
+       for policy in res:
+               acl = ndr_unpack(security.descriptor,str(policy["nTSecurityDescriptor"])).as_sddl()
+               policy_path = os.path.join(sysvol, names.dnsdomain, "Policies",
+                                                                        str(policy["cn"]))
+               set_gpo_acl(policy_path,dsacl2fsacl(acl,str(domainsid)),lp,str(domainsid))
+
 
 
 def provision(setup_dir, message, session_info, 
@@ -1136,7 +1122,7 @@ def provision(setup_dir, message, session_info,
               sitename=None,
               ol_mmr_urls=None, ol_olc=None, 
               setup_ds_path=None, slapd_path=None, nosync=False,
-              ldap_dryrun_mode=False):
+              ldap_dryrun_mode=False,useeadb=False):
     """Provision samba4
     
     :note: caution, this wipes all existing data!
@@ -1184,6 +1170,10 @@ def provision(setup_dir, message, session_info,
         wheel_gid = findnss_gid(["wheel", "adm"])
     else:
         wheel_gid = findnss_gid([wheel])
+    try:
+        bind_gid = findnss_gid(["bind", "named"])
+    except KeyError:
+        bind_gid = None
 
     if targetdir is not None:
         if (not os.path.exists(os.path.join(targetdir, "etc"))):
@@ -1193,9 +1183,18 @@ def provision(setup_dir, message, session_info,
         smbconf = param.default_path()
 
     # only install a new smb.conf if there isn't one there already
-    if not os.path.exists(smbconf):
+    if os.path.exists(smbconf):
+        # if Samba Team members can't figure out the weird errors
+        # loading an empty smb.conf gives, then we need to be smarter.
+        # Pretend it just didn't exist --abartlet
+        data = open(smbconf, 'r').read()
+        data = data.lstrip()
+        if data is None or data == "":
+            make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole, 
+                         targetdir, sid_generator, useeadb)
+    else: 
         make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole, 
-                     targetdir, sid_generator)
+                     targetdir, sid_generator, useeadb)
 
     lp = param.LoadParm()
     lp.load(smbconf)
@@ -1207,6 +1206,8 @@ def provision(setup_dir, message, session_info,
 
     paths = provision_paths_from_lp(lp, names.dnsdomain)
 
+    paths.bind_gid = bind_gid
+
     if hostip is None:
         try:
             hostip = socket.getaddrinfo(names.hostname, None, socket.AF_INET, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
@@ -1223,14 +1224,16 @@ def provision(setup_dir, message, session_info,
         serverrole = lp.get("server role")
 
     assert serverrole in ("domain controller", "member server", "standalone")
-    if invocationid is None and serverrole == "domain controller":
+    if invocationid is None:
         invocationid = str(uuid.uuid4())
 
     if not os.path.exists(paths.private_dir):
         os.mkdir(paths.private_dir)
+    if not os.path.exists(os.path.join(paths.private_dir,"tls")):
+        os.mkdir(os.path.join(paths.private_dir,"tls"))
 
     ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
-    
     schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn)
     
     if backend_type == "ldb":
@@ -1250,22 +1253,25 @@ def provision(setup_dir, message, session_info,
                                          paths=paths, setup_path=setup_path,
                                          lp=lp, credentials=credentials, 
                                          names=names,
-                                         message=message, hostname=hostname,
+                                         message=message,
+                                         domainsid=domainsid,
                                          schema=schema,
+                                         hostname=hostname,
                                          ldapadminpass=ldapadminpass,
                                          slapd_path=slapd_path,
                                          ldap_backend_extra_port=ldap_backend_extra_port,
                                          ldap_dryrun_mode=ldap_dryrun_mode,
                                          root=root,
-                                         setup_ds_path=setup_ds_path,
-                                         domainsid=domainsid)
+                                         setup_ds_path=setup_ds_path)
     elif backend_type == "openldap":
         provision_backend = OpenLDAPBackend(backend_type,
                                          paths=paths, setup_path=setup_path,
                                          lp=lp, credentials=credentials, 
                                          names=names,
-                                         message=message, hostname=hostname,
+                                         message=message,
+                                         domainsid=domainsid,
                                          schema=schema,
+                                         hostname=hostname,
                                          ldapadminpass=ldapadminpass,
                                          slapd_path=slapd_path,
                                          ldap_backend_extra_port=ldap_backend_extra_port,
@@ -1275,7 +1281,7 @@ def provision(setup_dir, message, session_info,
     else:
         raise ProvisioningError("Unknown LDAP backend type selected")
 
-    provision_backend.setup()
+    provision_backend.init()
     provision_backend.start()
 
     # only install a new shares config db if there is none
@@ -1329,23 +1335,6 @@ def provision(setup_dir, message, session_info,
                     (paths.smbconf, setup_path("provision.smb.conf.dc")))
             assert(paths.sysvol is not None)            
             
-        # Set up group policies (domain policy and domain controller policy)
-
-        policy_path = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
-                                   "{" + policyguid + "}")
-        os.makedirs(policy_path, 0755)
-        open(os.path.join(policy_path, "GPT.INI"), 'w').write(
-                                   "[General]\r\nVersion=65543")
-        os.makedirs(os.path.join(policy_path, "MACHINE"), 0755)
-        os.makedirs(os.path.join(policy_path, "USER"), 0755)
-
-        policy_path_dc = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
-                                   "{" + policyguid_dc + "}")
-        os.makedirs(policy_path_dc, 0755)
-        open(os.path.join(policy_path_dc, "GPT.INI"), 'w').write(
-                                   "[General]\r\nVersion=2")
-        os.makedirs(os.path.join(policy_path_dc, "MACHINE"), 0755)
-        os.makedirs(os.path.join(policy_path_dc, "USER"), 0755)
 
         if not os.path.isdir(paths.netlogon):
             os.makedirs(paths.netlogon, 0755)
@@ -1355,20 +1344,25 @@ def provision(setup_dir, message, session_info,
                             root_uid=root_uid, nobody_uid=nobody_uid,
                             users_gid=users_gid, wheel_gid=wheel_gid)
 
+        if serverrole == "domain controller":
+            # Set up group policies (domain policy and domain controller policy)
+            setup_gpo(paths,names,samdb,policyguid,policyguid_dc,domainsid)
+            setsysvolacl(samdb,names,paths.netlogon,paths.sysvol,wheel_gid,domainsid,lp)
+
         message("Setting up sam.ldb rootDSE marking as synchronized")
         setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
 
-        # Only make a zone file on the first DC, it should be replicated with DNS replication
+        secretsdb_self_join(secrets_ldb, domain=names.domain,
+                            realm=names.realm,
+                            dnsdomain=names.dnsdomain,
+                            netbiosname=names.netbiosname,
+                            domainsid=domainsid, 
+                            machinepass=machinepass,
+                            secure_channel_type=SEC_CHAN_BDC)
+
         if serverrole == "domain controller":
-            secretsdb_self_join(secrets_ldb, domain=domain,
-                                realm=names.realm,
-                                dnsdomain=names.dnsdomain,
-                                netbiosname=names.netbiosname,
-                                domainsid=domainsid, 
-                                machinepass=machinepass,
-                                secure_channel_type=SEC_CHAN_BDC)
-
-            secretsdb_setup_dns(secrets_ldb, setup_path, 
+            secretsdb_setup_dns(secrets_ldb, setup_path,
+                                paths.private_dir,
                                 realm=names.realm, dnsdomain=names.dnsdomain,
                                 dns_keytab_path=paths.dns_keytab,
                                 dnspass=dnspass)
@@ -1376,13 +1370,15 @@ def provision(setup_dir, message, session_info,
             domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
             assert isinstance(domainguid, str)
 
-            create_zone_file(paths.dns, setup_path, dnsdomain=names.dnsdomain,
+            # Only make a zone file on the first DC, it should be replicated
+            # with DNS replication
+            create_zone_file(message, paths, setup_path, dnsdomain=names.dnsdomain,
                              hostip=hostip,
                              hostip6=hostip6, hostname=names.hostname,
                              realm=names.realm,
                              domainguid=domainguid, ntdsguid=names.ntdsguid)
 
-            create_named_conf(paths.namedconf, setup_path, realm=names.realm,
+            create_named_conf(paths, setup_path, realm=names.realm,
                               dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
 
             create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
@@ -1405,6 +1401,16 @@ def provision(setup_dir, message, session_info,
     #Now commit the secrets.ldb to disk
     secrets_ldb.transaction_commit()
 
+    # the commit creates the dns.keytab, now chown it
+    dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
+    if (os.path.isfile(dns_keytab_path) and paths.bind_gid is not None):
+        try:
+            os.chmod(dns_keytab_path, 0640)
+            os.chown(dns_keytab_path, -1, paths.bind_gid)
+        except OSError:
+            message("Failed to chown %s to bind gid %u" % (dns_keytab_path, paths.bind_gid))
+
+
     message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
 
     message("Once the above files are installed, your Samba4 server will be ready to use")
@@ -1414,7 +1420,7 @@ def provision(setup_dir, message, session_info,
     message("DNS Domain:            %s" % names.dnsdomain)
     message("DOMAIN SID:            %s" % str(domainsid))
     if samdb_fill == FILL_FULL:
-        message("Admin password:    %s" % adminpass)
+        message("Admin password:        %s" % adminpass)
     if provision_backend.type is not "ldb":
         if provision_backend.credentials.get_bind_dn() is not None:
             message("LDAP Backend Admin DN: %s" % provision_backend.credentials.get_bind_dn())
@@ -1477,12 +1483,12 @@ def create_phpldapadmin_config(path, setup_path, ldapi_uri):
             {"S4_LDAPI_URI": ldapi_uri})
 
 
-def create_zone_file(path, setup_path, dnsdomain, 
+def create_zone_file(message, paths, setup_path, dnsdomain,
                      hostip, hostip6, hostname, realm, domainguid,
                      ntdsguid):
     """Write out a DNS zone file, from the info in the current database.
 
-    :param path: Path of the new zone file.
+    :param paths: paths object
     :param setup_path: Setup path function.
     :param dnsdomain: DNS Domain name
     :param domaindn: DN of the Domain
@@ -1509,7 +1515,22 @@ def create_zone_file(path, setup_path, dnsdomain,
         hostip_base_line = ""
         hostip_host_line = ""
 
-    setup_file(setup_path("provision.zone"), path, {
+    dns_dir = os.path.dirname(paths.dns)
+
+    try:
+        shutil.rmtree(dns_dir, True)
+    except OSError:
+        pass
+
+    os.mkdir(dns_dir, 0770)
+
+    if paths.bind_gid is not None:
+        try:
+            os.chown(dns_dir, -1, paths.bind_gid)
+        except OSError:
+            message("Failed to chown %s to bind gid %u" % (dns_dir, paths.bind_gid))
+
+    setup_file(setup_path("provision.zone"), paths.dns, {
             "HOSTNAME": hostname,
             "DNSDOMAIN": dnsdomain,
             "REALM": realm,
@@ -1524,12 +1545,12 @@ def create_zone_file(path, setup_path, dnsdomain,
         })
 
 
-def create_named_conf(path, setup_path, realm, dnsdomain,
+def create_named_conf(paths, setup_path, realm, dnsdomain,
                       private_dir):
     """Write out a file containing zone statements suitable for inclusion in a
     named.conf file (including GSS-TSIG configuration).
     
-    :param path: Path of the new named.conf file.
+    :param paths: all paths
     :param setup_path: Setup path function.
     :param realm: Realm name
     :param dnsdomain: DNS Domain name
@@ -1537,11 +1558,12 @@ def create_named_conf(path, setup_path, realm, dnsdomain,
     :param keytab_name: File name of DNS keytab file
     """
 
-    setup_file(setup_path("named.conf"), path, {
+    setup_file(setup_path("named.conf"), paths.namedconf, {
             "DNSDOMAIN": dnsdomain,
             "REALM": realm,
+            "ZONE_FILE": paths.dns,
             "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
-            "PRIVATE_DIR": private_dir
+            "NAMED_CONF": paths.namedconf
             })
 
 def create_named_txt(path, setup_path, realm, dnsdomain,