provision: setup names.dns_backend
[nivanova/samba-autobuild/.git] / source4 / scripting / python / samba / provision / __init__.py
index 48dd8673c7a159500364a7243595a67703a71966..507582bad590f492cc332a0a6166015d33a2cc63 100644 (file)
@@ -1,4 +1,3 @@
-
 # Unix SMB/CIFS implementation.
 # backend code for provisioning a Samba4 server
 
@@ -38,11 +37,14 @@ import uuid
 import socket
 import urllib
 import string
+import tempfile
 
 import ldb
 
 from samba.auth import system_session, admin_session
 import samba
+from samba.samba3 import smbd, passdb
+from samba.samba3 import param as s3param
 from samba.dsdb import DS_DOMAIN_FUNCTION_2000
 from samba import (
     Ldb,
@@ -66,7 +68,7 @@ from samba.dsdb import (
     )
 from samba.idmap import IDmapDB
 from samba.ms_display_specifiers import read_ms_ldif
-from samba.ntacls import setntacl, dsacl2fsacl
+from samba.ntacls import setntacl, getntacl, dsacl2fsacl
 from samba.ndr import ndr_pack, ndr_unpack
 from samba.provision.backend import (
     ExistingBackend,
@@ -75,8 +77,25 @@ from samba.provision.backend import (
     OpenLDAPBackend,
     )
 from samba.provision.descriptor import (
+    get_empty_descriptor,
     get_config_descriptor,
-    get_domain_descriptor
+    get_config_partitions_descriptor,
+    get_config_sites_descriptor,
+    get_config_ntds_quotas_descriptor,
+    get_config_delete_protected1_descriptor,
+    get_config_delete_protected1wd_descriptor,
+    get_config_delete_protected2_descriptor,
+    get_domain_descriptor,
+    get_domain_infrastructure_descriptor,
+    get_domain_builtin_descriptor,
+    get_domain_computers_descriptor,
+    get_domain_users_descriptor,
+    get_domain_controllers_descriptor,
+    get_domain_delete_protected1_descriptor,
+    get_domain_delete_protected2_descriptor,
+    get_dns_partition_descriptor,
+    get_dns_forest_microsoft_dns_descriptor,
+    get_dns_domain_microsoft_dns_descriptor,
     )
 from samba.provision.common import (
     setup_path,
@@ -84,6 +103,7 @@ from samba.provision.common import (
     setup_modify_ldif,
     )
 from samba.provision.sambadns import (
+    get_dnsadmins_sid,
     setup_ad_dns,
     create_dns_update_list
     )
@@ -119,16 +139,19 @@ class ProvisionPaths(object):
         self.dns = None
         self.winsdb = None
         self.private_dir = None
-        self.phpldapadminconfig = None
+        self.state_dir = None
 
 
 class ProvisionNames(object):
 
     def __init__(self):
+        self.ncs = None
         self.rootdn = None
         self.domaindn = None
         self.configdn = None
         self.schemadn = None
+        self.dnsforestdn = None
+        self.dnsdomaindn = None
         self.ldapmanagerdn = None
         self.dnsdomain = None
         self.realm = None
@@ -137,8 +160,11 @@ class ProvisionNames(object):
         self.hostname = None
         self.sitename = None
         self.smbconf = None
+        self.name_map = {}
+
 
-def find_provision_key_parameters(samdb, secretsdb, idmapdb, paths, smbconf, lp):
+def find_provision_key_parameters(samdb, secretsdb, idmapdb, paths, smbconf,
+        lp):
     """Get key provision parameters (realm, domain, ...) from a given provision
 
     :param samdb: An LDB object connected to the sam.ldb file
@@ -172,7 +198,8 @@ def find_provision_key_parameters(samdb, secretsdb, idmapdb, paths, smbconf, lp)
     current = samdb.search(expression="(objectClass=*)",
         base="", scope=ldb.SCOPE_BASE,
         attrs=["defaultNamingContext", "schemaNamingContext",
-               "configurationNamingContext","rootDomainNamingContext"])
+               "configurationNamingContext","rootDomainNamingContext",
+               "namingContexts"])
 
     names.configdn = current[0]["configurationNamingContext"]
     configdn = str(names.configdn)
@@ -186,6 +213,23 @@ def find_provision_key_parameters(samdb, secretsdb, idmapdb, paths, smbconf, lp)
 
     names.domaindn=current[0]["defaultNamingContext"]
     names.rootdn=current[0]["rootDomainNamingContext"]
+    names.ncs=current[0]["namingContexts"]
+    names.dnsforestdn = None
+    names.dnsdomaindn = None
+
+    for i in range(0, len(names.ncs)):
+        nc = names.ncs[i]
+
+        dnsforestdn = "DC=ForestDnsZones,%s" % (str(names.rootdn))
+        if nc == dnsforestdn:
+            names.dnsforestdn = dnsforestdn
+            continue
+
+        dnsdomaindn = "DC=DomainDnsZones,%s" % (str(names.domaindn))
+        if nc == dnsdomaindn:
+            names.dnsdomaindn = dnsdomaindn
+            continue
+
     # default site name
     res3 = samdb.search(expression="(objectClass=site)",
         base="CN=Sites," + configdn, scope=ldb.SCOPE_ONELEVEL, attrs=["cn"])
@@ -195,7 +239,7 @@ def find_provision_key_parameters(samdb, secretsdb, idmapdb, paths, smbconf, lp)
     res4 = samdb.search(expression="(CN=%s)" % names.netbiosname,
                             base="OU=Domain Controllers,%s" % basedn,
                             scope=ldb.SCOPE_ONELEVEL, attrs=["dNSHostName"])
-    names.hostname = str(res4[0]["dNSHostName"]).replace("." + names.dnsdomain,"")
+    names.hostname = str(res4[0]["dNSHostName"]).replace("." + names.dnsdomain, "")
 
     server_res = samdb.search(expression="serverReference=%s" % res4[0].dn,
                                 attrs=[], base=configdn)
@@ -203,7 +247,8 @@ def find_provision_key_parameters(samdb, secretsdb, idmapdb, paths, smbconf, lp)
 
     # invocation id/objectguid
     res5 = samdb.search(expression="(objectClass=*)",
-            base="CN=NTDS Settings,%s" % str(names.serverdn), scope=ldb.SCOPE_BASE,
+            base="CN=NTDS Settings,%s" % str(names.serverdn),
+            scope=ldb.SCOPE_BASE,
             attrs=["invocationID", "objectGUID"])
     names.invocation = str(ndr_unpack(misc.GUID, res5[0]["invocationId"][0]))
     names.ntdsguid = str(ndr_unpack(misc.GUID, res5[0]["objectGUID"][0]))
@@ -229,18 +274,52 @@ def find_provision_key_parameters(samdb, secretsdb, idmapdb, paths, smbconf, lp)
     res8 = samdb.search(expression="(displayName=Default Domain Controllers"
                                    " Policy)",
                             base="CN=Policies,CN=System," + basedn,
-                            scope=ldb.SCOPE_ONELEVEL, attrs=["cn","displayName"])
+                            scope=ldb.SCOPE_ONELEVEL,
+                            attrs=["cn","displayName"])
     if len(res8) == 1:
         names.policyid_dc = str(res8[0]["cn"]).replace("{","").replace("}","")
     else:
         names.policyid_dc = None
-    res9 = idmapdb.search(expression="(cn=%s)" %
-                            (security.SID_BUILTIN_ADMINISTRATORS),
-                            attrs=["xidNumber"])
-    if len(res9) == 1:
-        names.wheel_gid = res9[0]["xidNumber"]
+
+    res9 = idmapdb.search(expression="(cn=%s-%s)" %
+                          (str(names.domainsid), security.DOMAIN_RID_ADMINISTRATOR),
+                          attrs=["xidNumber", "type"])
+    if len(res9) != 1:
+        raise ProvisioningError("Unable to find uid/gid for Domain Admins rid (%s-%s" % (str(names.domainsid), security.DOMAIN_RID_ADMINISTRATOR))
+    if res9[0]["type"][0] == "ID_TYPE_BOTH":
+        names.root_gid = res9[0]["xidNumber"][0]
+    else:
+        names.root_gid = pwd.getpwuid(int(res9[0]["xidNumber"][0])).pw_gid
+
+    res10 = samdb.search(expression="(samaccountname=dns)",
+                         scope=ldb.SCOPE_SUBTREE, attrs=["dn"],
+                         controls=["search_options:1:2"])
+    if (len(res10) > 0):
+        has_legacy_dns_account = True
+    else:
+        has_legacy_dns_account = False
+
+    res11 = samdb.search(expression="(samaccountname=dns-%s)" % names.netbiosname,
+                         scope=ldb.SCOPE_SUBTREE, attrs=["dn"],
+                         controls=["search_options:1:2"])
+    if (len(res11) > 0):
+        has_dns_account = True
+    else:
+        has_dns_account = False
+
+    if names.dnsdomaindn is not None:
+        if has_dns_account:
+            names.dns_backend = 'BIND9_DLZ'
+        else:
+            names.dns_backend = 'SAMBA_INTERNAL'
+    elif has_dns_account or has_legacy_dns_account:
+        names.dns_backend = 'BIND9_FLATFILE'
     else:
-        raise ProvisioningError("Unable to find uid/gid for Domain Admins rid")
+        names.dns_backend = 'NONE'
+
+    dns_admins_sid = get_dnsadmins_sid(samdb, names.domaindn)
+    names.name_map['DnsAdmins'] = str(dns_admins_sid)
+
     return names
 
 
@@ -325,18 +404,18 @@ def get_last_provision_usn(sam):
     """Get USNs ranges modified by a provision or an upgradeprovision
 
     :param sam: An LDB object pointing to the sam.ldb
-    :return: a dictionnary which keys are invocation id and values are an array
+    :return: a dictionary which keys are invocation id and values are an array
              of integer representing the different ranges
     """
     try:
         entry = sam.search(expression="%s=*" % LAST_PROVISION_USN_ATTRIBUTE,
-                           base="@PROVISION", scope=ldb.SCOPE_BASE,
-                           attrs=[LAST_PROVISION_USN_ATTRIBUTE, "provisionnerID"])
+                       base="@PROVISION", scope=ldb.SCOPE_BASE,
+                       attrs=[LAST_PROVISION_USN_ATTRIBUTE, "provisionnerID"])
     except ldb.LdbError, (ecode, emsg):
         if ecode == ldb.ERR_NO_SUCH_OBJECT:
             return None
         raise
-    if len(entry):
+    if len(entry) > 0:
         myids = []
         range = {}
         p = re.compile(r'-')
@@ -352,7 +431,7 @@ def get_last_provision_usn(sam):
             if (len(myids) > 0 and id not in myids):
                 continue
             tab2 = p.split(tab1[0])
-            if range.get(id) == None:
+            if range.get(id) is None:
                 range[id] = []
             range[id].append(tab2[0])
             range[id].append(tab2[1])
@@ -395,12 +474,6 @@ class ProvisionResult(object):
         logger.info("DNS Domain:            %s", self.names.dnsdomain)
         logger.info("DOMAIN SID:            %s", self.domainsid)
 
-        if self.paths.phpldapadminconfig is not None:
-            logger.info(
-                "A phpLDAPadmin configuration file suitable for administering "
-                "the Samba 4 LDAP server has been created in %s.",
-                self.paths.phpldapadminconfig)
-
         if self.backend_result:
             self.backend_result.report_logger(logger)
 
@@ -447,6 +520,7 @@ def provision_paths_from_lp(lp, dnsdomain):
     """
     paths = ProvisionPaths()
     paths.private_dir = lp.get("private dir")
+    paths.state_dir = lp.get("state directory")
 
     # This is stored without path prefix for the "privateKeytab" attribute in
     # "secrets_dns.ldif".
@@ -467,8 +541,6 @@ def provision_paths_from_lp(lp, dnsdomain):
     paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
     paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
     paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
-    paths.phpldapadminconfig = os.path.join(paths.private_dir,
-                                            "phpldapadmin-config.php")
     paths.hklm = "hklm.ldb"
     paths.hkcr = "hkcr.ldb"
     paths.hkcu = "hkcu.ldb"
@@ -528,7 +600,7 @@ def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
     if lp.get("server role").lower() != serverrole:
         raise ProvisioningError("guess_names: 'server role=%s' in %s must match chosen server role '%s'!  Please remove the smb.conf file and let provision generate it" % (lp.get("server role"), lp.configfile, serverrole))
 
-    if serverrole == "domain controller":
+    if serverrole == "active directory domain controller":
         if domain is None:
             # This will, for better or worse, default to 'WORKGROUP'
             domain = lp.get("workgroup")
@@ -587,8 +659,8 @@ def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
 
 
 def make_smbconf(smbconf, hostname, domain, realm, targetdir,
-                 serverrole=None, sid_generator=None, eadb=False, lp=None,
-                 server_services=None):
+                 serverrole=None, eadb=False, use_ntvfs=False, lp=None,
+                 global_param=None):
     """Create a new smb.conf file based on a couple of basic settings.
     """
     assert smbconf is not None
@@ -599,10 +671,7 @@ def make_smbconf(smbconf, hostname, domain, realm, targetdir,
     netbiosname = determine_netbios_name(hostname)
 
     if serverrole is None:
-        serverrole = "standalone"
-
-    if sid_generator is None:
-        sid_generator = "internal"
+        serverrole = "standalone server"
 
     assert domain is not None
     domain = domain.upper()
@@ -611,7 +680,6 @@ def make_smbconf(smbconf, hostname, domain, realm, targetdir,
     realm = realm.upper()
 
     global_settings = {
-        "passdb backend": "samba4",
         "netbios name": netbiosname,
         "workgroup": domain,
         "realm": realm,
@@ -620,35 +688,46 @@ def make_smbconf(smbconf, hostname, domain, realm, targetdir,
 
     if lp is None:
         lp = samba.param.LoadParm()
-    #Load non-existant file
+    #Load non-existent file
     if os.path.exists(smbconf):
         lp.load(smbconf)
-    if eadb and not lp.get("posix:eadb"):
-        if targetdir is not None:
-            privdir = os.path.join(targetdir, "private")
-        else:
-            privdir = lp.get("private dir")
-        lp.set("posix:eadb", os.path.abspath(os.path.join(privdir, "eadb.tdb")))
 
-    if server_services is not None:
-        global_settings["server services"] = " ".join(server_services)
+    if global_param is not None:
+        for ent in global_param:
+            if global_param[ent] is not None:
+                global_settings[ent] = " ".join(global_param[ent])
 
     if targetdir is not None:
         global_settings["private dir"] = os.path.abspath(os.path.join(targetdir, "private"))
         global_settings["lock dir"] = os.path.abspath(targetdir)
-        global_settings["state directory"] = os.path.abspath(targetdir)
-        global_settings["cache directory"] = os.path.abspath(targetdir)
+        global_settings["state directory"] = os.path.abspath(os.path.join(targetdir, "state"))
+        global_settings["cache directory"] = os.path.abspath(os.path.join(targetdir, "cache"))
 
         lp.set("lock dir", os.path.abspath(targetdir))
-        lp.set("state directory", os.path.abspath(targetdir))
-        lp.set("cache directory", os.path.abspath(targetdir))
+        lp.set("state directory",  global_settings["state directory"])
+        lp.set("cache directory", global_settings["cache directory"])
+
+    if eadb:
+        if use_ntvfs and not lp.get("posix:eadb"):
+            if targetdir is not None:
+                privdir = os.path.join(targetdir, "private")
+            else:
+                privdir = lp.get("private dir")
+            lp.set("posix:eadb", os.path.abspath(os.path.join(privdir, "eadb.tdb")))
+        elif not use_ntvfs and not lp.get("xattr_tdb:file"):
+            if targetdir is not None:
+                statedir = os.path.join(targetdir, "state")
+            else:
+                statedir = lp.get("state directory")
+            lp.set("xattr_tdb:file", os.path.abspath(os.path.join(statedir, "xattr.tdb")))
 
     shares = {}
-    if serverrole == "domain controller":
-        shares["sysvol"] = os.path.join(global_settings["state directory"],
-            "sysvol")
+    if serverrole == "active directory domain controller":
+        shares["sysvol"] = os.path.join(lp.get("state directory"), "sysvol")
         shares["netlogon"] = os.path.join(shares["sysvol"], realm.lower(),
             "scripts")
+    else:
+        global_settings["passdb backend"] = "samba_dsdb"
 
     f = open(smbconf, 'w')
     try:
@@ -678,7 +757,7 @@ def make_smbconf(smbconf, hostname, domain, realm, targetdir,
 
 
 def setup_name_mappings(idmap, sid, root_uid, nobody_uid,
-                        users_gid, wheel_gid):
+                        users_gid, root_gid):
     """setup reasonable name mappings for sam names to unix names.
 
     :param samdb: SamDB object.
@@ -688,10 +767,9 @@ def setup_name_mappings(idmap, sid, root_uid, nobody_uid,
     :param root_uid: uid of the UNIX root user.
     :param nobody_uid: uid of the UNIX nobody user.
     :param users_gid: gid of the UNIX users group.
-    :param wheel_gid: gid of the UNIX wheel group.
+    :param root_gid: gid of the UNIX root group.
     """
     idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
-    idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
 
     idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
     idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
@@ -799,7 +877,7 @@ def secretsdb_self_join(secretsdb, domain,
     # but we don't delete the old record that we are about to modify,
     # because that would delete the keytab and previous password.
     res = secretsdb.search(base="cn=Primary Domains", attrs=attrs,
-        expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(dn=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
+        expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(distinguishedName=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
         scope=ldb.SCOPE_ONELEVEL)
 
     for del_msg in res:
@@ -955,7 +1033,8 @@ def setup_samdb_rootdse(samdb, names):
 
 
 def setup_self_join(samdb, admin_session_info, names, fill, machinepass,
-        dnspass, domainsid, next_rid, invocationid, policyguid, policyguid_dc,
+        dns_backend, dnspass, domainsid, next_rid, invocationid,
+        policyguid, policyguid_dc,
         domainControllerFunctionality, ntdsguid=None, dc_rid=None):
     """Join a host to its own domain."""
     assert isinstance(invocationid, str)
@@ -1033,9 +1112,10 @@ def setup_self_join(samdb, admin_session_info, names, fill, machinepass,
 
     samdb.set_session_info(admin_session_info)
 
-    # This is Samba4 specific and should be replaced by the correct
-    # DNS AD-style setup
-    setup_add_ldif(samdb, setup_path("provision_dns_add_samba.ldif"), {
+    if dns_backend != "SAMBA_INTERNAL":
+        # This is Samba4 specific and should be replaced by the correct
+        # DNS AD-style setup
+        setup_add_ldif(samdb, setup_path("provision_dns_add_samba.ldif"), {
               "DNSDOMAIN": names.dnsdomain,
               "DOMAINDN": names.domaindn,
               "DNSPASS_B64": b64encode(dnspass.encode('utf-16-le')),
@@ -1111,7 +1191,7 @@ def setup_samdb(path, session_info, provision_backend, lp, names,
     logger.info("Pre-loading the Samba 4 and AD schema")
 
     # Load the schema from the one we computed earlier
-    samdb.set_schema(schema)
+    samdb.set_schema(schema, write_indices_and_attributes=False)
 
     # Set the NTDS settings DN manually - in order to have it already around
     # before the provisioned tree exists and we connect
@@ -1121,14 +1201,18 @@ def setup_samdb(path, session_info, provision_backend, lp, names,
     # DB
     samdb.connect(path)
 
+    # But we have to give it one more kick to have it use the schema
+    # during provision - it needs, now that it is connected, to write
+    # the schema @ATTRIBUTES and @INDEXLIST records to the database.
+    samdb.set_schema(schema, write_indices_and_attributes=True)
+
     return samdb
 
 
-def fill_samdb(samdb, lp, names,
-        logger, domainsid, domainguid, policyguid, policyguid_dc, fill,
-        adminpass, krbtgtpass, machinepass, invocationid, dnspass, ntdsguid,
-        serverrole, am_rodc=False, dom_for_fun_level=None, schema=None,
-        next_rid=None, dc_rid=None):
+def fill_samdb(samdb, lp, names, logger, domainsid, domainguid, policyguid,
+        policyguid_dc, fill, adminpass, krbtgtpass, machinepass, dns_backend,
+        dnspass, invocationid, ntdsguid, serverrole, am_rodc=False,
+        dom_for_fun_level=None, schema=None, next_rid=None, dc_rid=None):
 
     if next_rid is None:
         next_rid = 1000
@@ -1240,6 +1324,14 @@ def fill_samdb(samdb, lp, names,
         # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
         if fill == FILL_FULL:
             logger.info("Setting up sam.ldb configuration data")
+
+            partitions_descr = b64encode(get_config_partitions_descriptor(domainsid))
+            sites_descr = b64encode(get_config_sites_descriptor(domainsid))
+            ntdsquotas_descr = b64encode(get_config_ntds_quotas_descriptor(domainsid))
+            protected1_descr = b64encode(get_config_delete_protected1_descriptor(domainsid))
+            protected1wd_descr = b64encode(get_config_delete_protected1wd_descriptor(domainsid))
+            protected2_descr = b64encode(get_config_delete_protected2_descriptor(domainsid))
+
             setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
                     "CONFIGDN": names.configdn,
                     "NETBIOSNAME": names.netbiosname,
@@ -1251,6 +1343,14 @@ def fill_samdb(samdb, lp, names,
                     "SERVERDN": names.serverdn,
                     "FOREST_FUNCTIONALITY": str(forestFunctionality),
                     "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
+                    "NTDSQUOTAS_DESCRIPTOR": ntdsquotas_descr,
+                    "LOSTANDFOUND_DESCRIPTOR": protected1wd_descr,
+                    "SERVICES_DESCRIPTOR": protected1_descr,
+                    "PHYSICALLOCATIONS_DESCRIPTOR": protected1wd_descr,
+                    "FORESTUPDATES_DESCRIPTOR": protected1wd_descr,
+                    "EXTENDEDRIGHTS_DESCRIPTOR": protected2_descr,
+                    "PARTITIONS_DESCRIPTOR": partitions_descr,
+                    "SITES_DESCRIPTOR": sites_descr,
                     })
 
             logger.info("Setting up display specifiers")
@@ -1261,20 +1361,38 @@ def fill_samdb(samdb, lp, names,
             check_all_substituted(display_specifiers_ldif)
             samdb.add_ldif(display_specifiers_ldif)
 
+            logger.info("Modifying display specifiers")
+            setup_modify_ldif(samdb,
+                setup_path("provision_configuration_modify.ldif"), {
+                "CONFIGDN": names.configdn,
+                "DISPLAYSPECIFIERS_DESCRIPTOR": protected2_descr
+                })
+
         logger.info("Adding users container")
+        users_desc = b64encode(get_domain_users_descriptor(domainsid))
         setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
-                "DOMAINDN": names.domaindn})
+                "DOMAINDN": names.domaindn,
+                "USERS_DESCRIPTOR": users_desc
+                })
         logger.info("Modifying users container")
         setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
                 "DOMAINDN": names.domaindn})
         logger.info("Adding computers container")
+        computers_desc = b64encode(get_domain_computers_descriptor(domainsid))
         setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
-                "DOMAINDN": names.domaindn})
+                "DOMAINDN": names.domaindn,
+                "COMPUTERS_DESCRIPTOR": computers_desc
+                })
         logger.info("Modifying computers container")
         setup_modify_ldif(samdb,
             setup_path("provision_computers_modify.ldif"), {
                 "DOMAINDN": names.domaindn})
         logger.info("Setting up sam.ldb data")
+        infrastructure_desc = b64encode(get_domain_infrastructure_descriptor(domainsid))
+        lostandfound_desc = b64encode(get_domain_delete_protected2_descriptor(domainsid))
+        system_desc = b64encode(get_domain_delete_protected1_descriptor(domainsid))
+        builtin_desc = b64encode(get_domain_builtin_descriptor(domainsid))
+        controllers_desc = b64encode(get_domain_controllers_descriptor(domainsid))
         setup_add_ldif(samdb, setup_path("provision.ldif"), {
             "CREATTIME": str(samba.unix2nttime(int(time.time()))),
             "DOMAINDN": names.domaindn,
@@ -1283,7 +1401,12 @@ def fill_samdb(samdb, lp, names,
             "CONFIGDN": names.configdn,
             "SERVERDN": names.serverdn,
             "RIDAVAILABLESTART": str(next_rid + 600),
-            "POLICYGUID_DC": policyguid_dc
+            "POLICYGUID_DC": policyguid_dc,
+            "INFRASTRUCTURE_DESCRIPTOR": infrastructure_desc,
+            "LOSTANDFOUND_DESCRIPTOR": lostandfound_desc,
+            "SYSTEM_DESCRIPTOR": system_desc,
+            "BUILTIN_DESCRIPTOR": builtin_desc,
+            "DOMAIN_CONTROLLERS_DESCRIPTOR": controllers_desc,
             })
 
         # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
@@ -1294,8 +1417,10 @@ def fill_samdb(samdb, lp, names,
                     "SCHEMADN": names.schemadn})
 
             logger.info("Setting up well known security principals")
+            protected1wd_descr = b64encode(get_config_delete_protected1wd_descriptor(domainsid))
             setup_add_ldif(samdb, setup_path("provision_well_known_sec_princ.ldif"), {
                 "CONFIGDN": names.configdn,
+                "WELLKNOWNPRINCIPALS_DESCRIPTOR": protected1wd_descr,
                 })
 
         if fill == FILL_FULL or fill == FILL_SUBDOMAIN:
@@ -1314,6 +1439,7 @@ def fill_samdb(samdb, lp, names,
             logger.info("Setting up self join")
             setup_self_join(samdb, admin_session_info, names=names, fill=fill,
                 invocationid=invocationid,
+                dns_backend=dns_backend,
                 dnspass=dnspass,
                 machinepass=machinepass,
                 domainsid=domainsid,
@@ -1342,18 +1468,20 @@ FILL_NT4SYNC = "NT4SYNC"
 FILL_DRS = "DRS"
 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
 POLICIES_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)(A;OICI;0x001301bf;;;PA)"
+SYSVOL_SERVICE="sysvol"
 
-
-def set_dir_acl(path, acl, lp, domsid):
-    setntacl(lp, path, acl, domsid)
+def set_dir_acl(path, acl, lp, domsid, use_ntvfs, passdb, service=SYSVOL_SERVICE):
+    setntacl(lp, path, acl, domsid, use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
     for root, dirs, files in os.walk(path, topdown=False):
         for name in files:
-            setntacl(lp, os.path.join(root, name), acl, domsid)
+            setntacl(lp, os.path.join(root, name), acl, domsid,
+                    use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
         for name in dirs:
-            setntacl(lp, os.path.join(root, name), acl, domsid)
+            setntacl(lp, os.path.join(root, name), acl, domsid,
+                    use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
 
 
-def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp):
+def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb):
     """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
     folders beneath.
 
@@ -1367,7 +1495,8 @@ def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp):
 
     # Set ACL for GPO root folder
     root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
-    setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid))
+    setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid),
+            use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=SYSVOL_SERVICE)
 
     res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
                         attrs=["cn", "nTSecurityDescriptor"],
@@ -1377,48 +1506,197 @@ def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp):
         acl = ndr_unpack(security.descriptor,
                          str(policy["nTSecurityDescriptor"])).as_sddl()
         policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
-        set_dir_acl(policy_path, dsacl2fsacl(acl, str(domainsid)), lp,
-                    str(domainsid))
+        set_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
+                    str(domainsid), use_ntvfs,
+                    passdb=passdb)
 
 
-def setsysvolacl(samdb, netlogon, sysvol, gid, domainsid, dnsdomain, domaindn,
-    lp):
+def setsysvolacl(samdb, netlogon, sysvol, uid, gid, domainsid, dnsdomain,
+        domaindn, lp, use_ntvfs):
     """Set the ACL for the sysvol share and the subfolders
 
     :param samdb: An LDB object on the SAM db
     :param netlogon: Physical path for the netlogon folder
     :param sysvol: Physical path for the sysvol folder
+    :param uid: The UID of the "Administrator" user
     :param gid: The GID of the "Domain adminstrators" group
     :param domainsid: The SID of the domain
     :param dnsdomain: The DNS name of the domain
     :param domaindn: The DN of the domain (ie. DC=...)
     """
+    s4_passdb = None
+
+    if not use_ntvfs:
+        # This will ensure that the smbd code we are running when setting ACLs
+        # is initialised with the smb.conf
+        s3conf = s3param.get_context()
+        s3conf.load(lp.configfile)
+        # ensure we are using the right samba_dsdb passdb backend, no matter what
+        s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
+        passdb.reload_static_pdb()
+
+        # ensure that we init the samba_dsdb backend, so the domain sid is
+        # marked in secrets.tdb
+        s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
+
+        # now ensure everything matches correctly, to avoid wierd issues
+        if passdb.get_global_sam_sid() != domainsid:
+            raise ProvisioningError('SID as seen by smbd [%s] does not match SID as seen by the provision script [%s]!' % (passdb.get_global_sam_sid(), domainsid))
+
+        domain_info = s4_passdb.domain_info()
+        if domain_info["dom_sid"] != domainsid:
+            raise ProvisioningError('SID as seen by pdb_samba_dsdb [%s] does not match SID as seen by the provision script [%s]!' % (domain_info["dom_sid"], domainsid))
+
+        if domain_info["dns_domain"].upper() != dnsdomain.upper():
+            raise ProvisioningError('Realm as seen by pdb_samba_dsdb [%s] does not match Realm as seen by the provision script [%s]!' % (domain_info["dns_domain"].upper(), dnsdomain.upper()))
+
 
     try:
-        os.chown(sysvol, -1, gid)
+        if use_ntvfs:
+            os.chown(sysvol, -1, gid)
     except OSError:
         canchown = False
     else:
         canchown = True
 
     # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
-    setntacl(lp,sysvol, SYSVOL_ACL, str(domainsid))
+    setntacl(lp,sysvol, SYSVOL_ACL, str(domainsid), use_ntvfs=use_ntvfs,
+             skip_invalid_chown=True, passdb=s4_passdb,
+             service=SYSVOL_SERVICE)
     for root, dirs, files in os.walk(sysvol, topdown=False):
         for name in files:
-            if canchown:
+            if use_ntvfs and canchown:
                 os.chown(os.path.join(root, name), -1, gid)
-            setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid))
+            setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid),
+                     use_ntvfs=use_ntvfs, skip_invalid_chown=True,
+                     passdb=s4_passdb, service=SYSVOL_SERVICE)
         for name in dirs:
-            if canchown:
+            if use_ntvfs and canchown:
                 os.chown(os.path.join(root, name), -1, gid)
-            setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid))
+            setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid),
+                     use_ntvfs=use_ntvfs, skip_invalid_chown=True,
+                     passdb=s4_passdb, service=SYSVOL_SERVICE)
 
     # Set acls on Policy folder and policies folders
-    set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp)
+    set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb=s4_passdb)
+
+def acl_type(direct_db_access):
+    if direct_db_access:
+        return "DB"
+    else:
+        return "VFS"
+
+def check_dir_acl(path, acl, lp, domainsid, direct_db_access):
+    fsacl = getntacl(lp, path, direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
+    fsacl_sddl = fsacl.as_sddl(domainsid)
+    if fsacl_sddl != acl:
+        raise ProvisioningError('%s ACL on GPO directory %s %s does not match expected value %s from GPO object' % (acl_type(direct_db_access), path, fsacl_sddl, acl))
+
+    for root, dirs, files in os.walk(path, topdown=False):
+        for name in files:
+            fsacl = getntacl(lp, os.path.join(root, name),
+                             direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
+            if fsacl is None:
+                raise ProvisioningError('%s ACL on GPO file %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name)))
+            fsacl_sddl = fsacl.as_sddl(domainsid)
+            if fsacl_sddl != acl:
+                raise ProvisioningError('%s ACL on GPO file %s %s does not match expected value %s from GPO object' % (acl_type(direct_db_access), os.path.join(root, name), fsacl_sddl, acl))
+
+        for name in dirs:
+            fsacl = getntacl(lp, os.path.join(root, name),
+                             direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
+            if fsacl is None:
+                raise ProvisioningError('%s ACL on GPO directory %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name)))
+            fsacl_sddl = fsacl.as_sddl(domainsid)
+            if fsacl_sddl != acl:
+                raise ProvisioningError('%s ACL on GPO directory %s %s does not match expected value %s from GPO object' % (acl_type(direct_db_access), os.path.join(root, name), fsacl_sddl, acl))
+
+
+def check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
+        direct_db_access):
+    """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
+    folders beneath.
+
+    :param sysvol: Physical path for the sysvol folder
+    :param dnsdomain: The DNS name of the domain
+    :param domainsid: The SID of the domain
+    :param domaindn: The DN of the domain (ie. DC=...)
+    :param samdb: An LDB object on the SAM db
+    :param lp: an LP object
+    """
+
+    # Set ACL for GPO root folder
+    root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
+    fsacl = getntacl(lp, root_policy_path,
+                     direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
+    if fsacl is None:
+        raise ProvisioningError('DB ACL on policy root %s %s not found!' % (acl_type(direct_db_access), root_policy_path))
+    fsacl_sddl = fsacl.as_sddl(domainsid)
+    if fsacl_sddl != POLICIES_ACL:
+        raise ProvisioningError('%s ACL on policy root %s %s does not match expected value %s from provision' % (acl_type(direct_db_access), root_policy_path, fsacl_sddl, fsacl))
+    res = samdb.search(base="CN=Policies,CN=System,%s"%(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 = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
+        check_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
+                      domainsid, direct_db_access)
+
+
+def checksysvolacl(samdb, netlogon, sysvol, domainsid, dnsdomain, domaindn,
+    lp):
+    """Set the ACL for the sysvol share and the subfolders
+
+    :param samdb: An LDB object on the SAM db
+    :param netlogon: Physical path for the netlogon folder
+    :param sysvol: Physical path for the sysvol folder
+    :param uid: The UID of the "Administrator" user
+    :param gid: The GID of the "Domain adminstrators" group
+    :param domainsid: The SID of the domain
+    :param dnsdomain: The DNS name of the domain
+    :param domaindn: The DN of the domain (ie. DC=...)
+    """
+
+    # This will ensure that the smbd code we are running when setting ACLs is initialised with the smb.conf
+    s3conf = s3param.get_context()
+    s3conf.load(lp.configfile)
+    # ensure we are using the right samba_dsdb passdb backend, no matter what
+    s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
+    # ensure that we init the samba_dsdb backend, so the domain sid is marked in secrets.tdb
+    s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
+
+    # now ensure everything matches correctly, to avoid wierd issues
+    if passdb.get_global_sam_sid() != domainsid:
+        raise ProvisioningError('SID as seen by smbd [%s] does not match SID as seen by the provision script [%s]!' % (passdb.get_global_sam_sid(), domainsid))
+
+    domain_info = s4_passdb.domain_info()
+    if domain_info["dom_sid"] != domainsid:
+        raise ProvisioningError('SID as seen by pdb_samba_dsdb [%s] does not match SID as seen by the provision script [%s]!' % (domain_info["dom_sid"], domainsid))
+
+    if domain_info["dns_domain"].upper() != dnsdomain.upper():
+        raise ProvisioningError('Realm as seen by pdb_samba_dsdb [%s] does not match Realm as seen by the provision script [%s]!' % (domain_info["dns_domain"].upper(), dnsdomain.upper()))
+
+    # Ensure we can read this directly, and via the smbd VFS
+    for direct_db_access in [True, False]:
+        # Check the SYSVOL_ACL on the sysvol folder and subfolder (first level)
+        for dir_path in [os.path.join(sysvol, dnsdomain), netlogon]:
+            fsacl = getntacl(lp, dir_path, direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
+            if fsacl is None:
+                raise ProvisioningError('%s ACL on sysvol directory %s not found!' % (acl_type(direct_db_access), dir_path))
+            fsacl_sddl = fsacl.as_sddl(domainsid)
+            if fsacl_sddl != SYSVOL_ACL:
+                raise ProvisioningError('%s ACL on sysvol directory %s %s does not match expected value %s from provision' % (acl_type(direct_db_access), dir_path, fsacl_sddl, SYSVOL_ACL))
+
+        # Check acls on Policy folder and policies folders
+        check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
+                direct_db_access)
 
 
 def interface_ips_v4(lp):
-    '''return only IPv4 IPs'''
+    """return only IPv4 IPs"""
     ips = samba.interface_ips(lp, False)
     ret = []
     for i in ips:
@@ -1426,8 +1704,9 @@ def interface_ips_v4(lp):
             ret.append(i)
     return ret
 
+
 def interface_ips_v6(lp, linklocal=False):
-    '''return only IPv6 IPs'''
+    """return only IPv6 IPs"""
     ips = samba.interface_ips(lp, False)
     ret = []
     for i in ips:
@@ -1445,7 +1724,7 @@ def provision_fill(samdb, secrets_ldb, logger, names, paths,
                    invocationid=None, machinepass=None, ntdsguid=None,
                    dns_backend=None, dnspass=None,
                    serverrole=None, dom_for_fun_level=None,
-                   am_rodc=False, lp=None):
+                   am_rodc=False, lp=None, use_ntvfs=False, skip_sysvolacl=False):
     # create/adapt the group policy GUIDs
     # Default GUID for default policy are described at
     # "How Core Group Policy Works"
@@ -1468,26 +1747,32 @@ def provision_fill(samdb, secrets_ldb, logger, names, paths,
         dnspass = samba.generate_random_password(128, 255)
 
     samdb = fill_samdb(samdb, lp, names, logger=logger,
-                       domainsid=domainsid, schema=schema, domainguid=domainguid,
-                       policyguid=policyguid, policyguid_dc=policyguid_dc,
-                       fill=samdb_fill, adminpass=adminpass, krbtgtpass=krbtgtpass,
-                       invocationid=invocationid, machinepass=machinepass,
-                       dnspass=dnspass, ntdsguid=ntdsguid, serverrole=serverrole,
-                       dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
-                       next_rid=next_rid, dc_rid=dc_rid)
-
-    if serverrole == "domain controller":
+                   domainsid=domainsid, schema=schema, domainguid=domainguid,
+                   policyguid=policyguid, policyguid_dc=policyguid_dc,
+                   fill=samdb_fill, adminpass=adminpass, krbtgtpass=krbtgtpass,
+                   invocationid=invocationid, machinepass=machinepass,
+                   dns_backend=dns_backend, dnspass=dnspass,
+                   ntdsguid=ntdsguid, serverrole=serverrole,
+                   dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
+                   next_rid=next_rid, dc_rid=dc_rid)
+
+    if serverrole == "active directory domain controller":
+
         # Set up group policies (domain policy and domain controller
         # policy)
         create_default_gpo(paths.sysvol, names.dnsdomain, policyguid,
                            policyguid_dc)
-        setsysvolacl(samdb, paths.netlogon, paths.sysvol, paths.wheel_gid,
-                     domainsid, names.dnsdomain, names.domaindn, lp)
+        if not skip_sysvolacl:
+            setsysvolacl(samdb, paths.netlogon, paths.sysvol, paths.root_uid,
+                         paths.root_gid, domainsid, names.dnsdomain,
+                         names.domaindn, lp, use_ntvfs)
+        else:
+            logger.info("Setting acl on sysvol skipped")
 
         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)
+                realm=names.realm, dnsdomain=names.dnsdomain,
+                netbiosname=names.netbiosname, domainsid=domainsid,
+                machinepass=machinepass, secure_channel_type=SEC_CHAN_BDC)
 
         # Now set up the right msDS-SupportedEncryptionTypes into the DB
         # In future, this might be determined from some configuration
@@ -1537,7 +1822,8 @@ def provision_fill(samdb, secrets_ldb, logger, names, paths,
         # 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'])
+                               scope=ldb.SCOPE_BASE,
+                               attrs=['defaultObjectCategory'])
         chk.check_database(DN="CN=IP Security,CN=System,%s" % names.domaindn,
                            scope=ldb.SCOPE_ONELEVEL,
                            attrs=['ipsecOwnersReference',
@@ -1553,15 +1839,17 @@ def provision_fill(samdb, secrets_ldb, logger, names, paths,
 
 
 _ROLES_MAP = {
-    "ROLE_STANDALONE": "standalone",
+    "ROLE_STANDALONE": "standalone server",
     "ROLE_DOMAIN_MEMBER": "member server",
-    "ROLE_DOMAIN_BDC": "domain controller",
-    "ROLE_DOMAIN_PDC": "domain controller",
-    "dc": "domain controller",
+    "ROLE_DOMAIN_BDC": "active directory domain controller",
+    "ROLE_DOMAIN_PDC": "active directory domain controller",
+    "dc": "active directory domain controller",
     "member": "member server",
-    "domain controller": "domain controller",
+    "domain controller": "active directory domain controller",
+    "active directory domain controller": "active directory domain controller",
     "member server": "member server",
-    "standalone": "standalone",
+    "standalone": "standalone server",
+    "standalone server": "standalone server",
     }
 
 
@@ -1571,28 +1859,48 @@ def sanitize_server_role(role):
     :param role: Server role
     :raise ValueError: If the role can not be interpreted
     :return: Sanitized server role (one of "member server",
-        "domain controller", "standalone")
+        "active directory domain controller", "standalone server")
     """
     try:
-        return  _ROLES_MAP[role]
+        return _ROLES_MAP[role]
     except KeyError:
         raise ValueError(role)
 
 
+def provision_fake_ypserver(logger, samdb, domaindn, netbiosname, nisdomain,
+        maxuid, maxgid):
+    """Create AD entries for the fake ypserver.
+
+    This is needed for being able to manipulate posix attrs via ADUC.
+    """
+    samdb.transaction_start()
+    try:
+        logger.info("Setting up fake yp server settings")
+        setup_add_ldif(samdb, setup_path("ypServ30.ldif"), {
+        "DOMAINDN": domaindn,
+        "NETBIOSNAME": netbiosname,
+        "NISDOMAIN": nisdomain,
+         })
+    except:
+        samdb.transaction_cancel()
+        raise
+    else:
+        samdb.transaction_commit()
+
+
 def provision(logger, session_info, credentials, smbconf=None,
         targetdir=None, samdb_fill=FILL_FULL, realm=None, rootdn=None,
         domaindn=None, schemadn=None, configdn=None, serverdn=None,
         domain=None, hostname=None, hostip=None, hostip6=None, domainsid=None,
-        next_rid=1000, dc_rid=None, adminpass=None, ldapadminpass=None, krbtgtpass=None,
-        domainguid=None, policyguid=None, policyguid_dc=None,
-        dns_backend=None, dnspass=None,
+        next_rid=1000, dc_rid=None, adminpass=None, ldapadminpass=None,
+        krbtgtpass=None, domainguid=None, policyguid=None, policyguid_dc=None,
+        dns_backend=None, dns_forwarder=None, dnspass=None,
         invocationid=None, machinepass=None, ntdsguid=None,
-        root=None, nobody=None, users=None, wheel=None, backup=None, aci=None,
-        serverrole=None, dom_for_fun_level=None, 
-        backend_type=None, sitename=None,
-        ol_mmr_urls=None, ol_olc=None, slapd_path=None,
-        useeadb=False, am_rodc=False,
-        lp=None):
+        root=None, nobody=None, users=None, backup=None, aci=None,
+        serverrole=None, dom_for_fun_level=None, backend_type=None,
+        sitename=None, ol_mmr_urls=None, ol_olc=None, slapd_path="/bin/false",
+        useeadb=False, am_rodc=False, lp=None, use_ntvfs=False,
+        use_rfc2307=False, maxuid=None, maxgid=None, skip_sysvolacl=True):
     """Provision samba4
 
     :note: caution, this wipes all existing data!
@@ -1601,7 +1909,7 @@ def provision(logger, session_info, credentials, smbconf=None,
     try:
         serverrole = sanitize_server_role(serverrole)
     except ValueError:
-        raise ProvisioningError('server role (%s) should be one of "domain controller", "member server", "standalone"' % serverrole)
+        raise ProvisioningError('server role (%s) should be one of "active directory domain controller", "member server", "standalone server"' % serverrole)
 
     if ldapadminpass is None:
         # Make a new, random password between Samba and it's LDAP server
@@ -1615,17 +1923,11 @@ def provision(logger, session_info, credentials, smbconf=None,
     else:
         domainsid = security.dom_sid(domainsid)
 
-    sid_generator = "internal"
-    if backend_type == "fedora-ds":
-        sid_generator = "backend"
-
     root_uid = findnss_uid([root or "root"])
     nobody_uid = findnss_uid([nobody or "nobody"])
     users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
-    if wheel is None:
-        wheel_gid = findnss_gid(["wheel", "adm"])
-    else:
-        wheel_gid = findnss_gid([wheel])
+    root_gid = pwd.getpwuid(root_uid).pw_gid
+
     try:
         bind_gid = findnss_gid(["bind", "named"])
     except KeyError:
@@ -1638,9 +1940,24 @@ def provision(logger, session_info, credentials, smbconf=None,
     if not os.path.exists(os.path.dirname(smbconf)):
         os.makedirs(os.path.dirname(smbconf))
 
-    server_services = None
-    if dns_backend == "SAMBA_INTERNAL":
-        server_services = ["+dns"]
+    server_services = []
+    global_param = {}
+    if use_rfc2307:
+        global_param["idmap_ldb:use rfc2307"] = ["yes"]
+
+    if dns_backend != "SAMBA_INTERNAL":
+        server_services.append("-dns")
+    else:
+        if dns_forwarder is not None:
+            global_param["dns forwarder"] = [dns_forwarder]
+
+    if use_ntvfs:
+        server_services.append("+smb")
+        server_services.append("-s3fs")
+        global_param["dcerpc endpoint servers"] = ["+winreg", "+srvsvc"]
+
+    if len(server_services) > 0:
+        global_param["server services"] = server_services
 
     # only install a new smb.conf if there isn't one there already
     if os.path.exists(smbconf):
@@ -1655,12 +1972,12 @@ def provision(logger, session_info, credentials, smbconf=None,
         if data is None or data == "":
             make_smbconf(smbconf, hostname, domain, realm,
                          targetdir, serverrole=serverrole,
-                         sid_generator=sid_generator, eadb=useeadb,
-                         lp=lp, server_services=server_services)
+                         eadb=useeadb, use_ntvfs=use_ntvfs,
+                         lp=lp, global_param=global_param)
     else:
         make_smbconf(smbconf, hostname, domain, realm, targetdir,
-                     serverrole=serverrole, sid_generator=sid_generator,
-                     eadb=useeadb, lp=lp, server_services=server_services)
+                     serverrole=serverrole,
+                     eadb=useeadb, use_ntvfs=use_ntvfs, lp=lp, global_param=global_param)
 
     if lp is None:
         lp = samba.param.LoadParm()
@@ -1672,7 +1989,8 @@ def provision(logger, session_info, credentials, smbconf=None,
     paths = provision_paths_from_lp(lp, names.dnsdomain)
 
     paths.bind_gid = bind_gid
-    paths.wheel_gid = wheel_gid
+    paths.root_uid = root_uid;
+    paths.root_gid = root_gid
 
     if hostip is None:
         logger.info("Looking up IPv4 addresses")
@@ -1707,6 +2025,37 @@ def provision(logger, session_info, credentials, smbconf=None,
         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"))
+    if not os.path.exists(paths.state_dir):
+        os.mkdir(paths.state_dir)
+
+    if paths.sysvol and not os.path.exists(paths.sysvol):
+        os.makedirs(paths.sysvol, 0775)
+
+    if not use_ntvfs and serverrole == "active directory domain controller":
+        s3conf = s3param.get_context()
+        s3conf.load(lp.configfile)
+
+        if paths.sysvol is None:
+            raise MissingShareError("sysvol", paths.smbconf)
+
+        file = tempfile.NamedTemporaryFile(dir=os.path.abspath(paths.sysvol))
+        try:
+            try:
+                smbd.set_simple_acl(file.name, 0755, root_gid)
+            except Exception:
+                if not smbd.have_posix_acls():
+                    # This clue is only strictly correct for RPM and
+                    # Debian-like Linux systems, but hopefully other users
+                    # will get enough clue from it.
+                    raise ProvisioningError("Samba was compiled without the posix ACL support that s3fs requires.  Try installing libacl1-dev or libacl-devel, then re-run configure and make.")
+
+                raise ProvisioningError("Your filesystem or build does not support posix ACLs, which s3fs requires.  Try the mounting the filesystem with the 'acl' option.")
+            try:
+                smbd.chown(file.name, root_uid, root_gid)
+            except Exception:
+                raise ProvisioningError("Unable to chown a file on your filesystem.  You may not be running provision as root.")
+        finally:
+            file.close()
 
     ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
 
@@ -1718,7 +2067,8 @@ def provision(logger, session_info, credentials, smbconf=None,
             lp=lp, credentials=credentials,
             names=names, logger=logger)
     elif backend_type == "existing":
-        # If support for this is ever added back, then the URI will need to be specified again
+        # If support for this is ever added back, then the URI will need to be
+        # specified again
         provision_backend = ExistingBackend(backend_type, paths=paths,
             lp=lp, credentials=credentials,
             names=names, logger=logger,
@@ -1765,7 +2115,7 @@ def provision(logger, session_info, credentials, smbconf=None,
 
         setup_name_mappings(idmap, sid=str(domainsid),
                             root_uid=root_uid, nobody_uid=nobody_uid,
-                            users_gid=users_gid, wheel_gid=wheel_gid)
+                            users_gid=users_gid, root_gid=root_gid)
 
         logger.info("Setting up SAM db")
         samdb = setup_samdb(paths.samdb, session_info,
@@ -1773,14 +2123,12 @@ def provision(logger, session_info, credentials, smbconf=None,
                             serverrole=serverrole,
                             schema=schema, fill=samdb_fill, am_rodc=am_rodc)
 
-        if serverrole == "domain controller":
+        if serverrole == "active directory domain controller":
             if paths.netlogon is None:
-                raise MissingShareError("netlogon", paths.smbconf,
-                    setup_path("provision.smb.conf.dc"))
+                raise MissingShareError("netlogon", paths.smbconf)
 
             if paths.sysvol is None:
-                raise MissingShareError("sysvol", paths.smbconf,
-                    setup_path("provision.smb.conf.dc"))
+                raise MissingShareError("sysvol", paths.smbconf)
 
             if not os.path.isdir(paths.netlogon):
                 os.makedirs(paths.netlogon, 0755)
@@ -1802,7 +2150,8 @@ def provision(logger, session_info, credentials, smbconf=None,
                     ntdsguid=ntdsguid, dns_backend=dns_backend,
                     dnspass=dnspass, serverrole=serverrole,
                     dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
-                    lp=lp)
+                    lp=lp, use_ntvfs=use_ntvfs,
+                           skip_sysvolacl=skip_sysvolacl)
 
         create_krb5_conf(paths.krb5conf,
                          dnsdomain=names.dnsdomain, hostname=names.hostname,
@@ -1810,14 +2159,12 @@ def provision(logger, session_info, credentials, smbconf=None,
         logger.info("A Kerberos configuration suitable for Samba 4 has been "
                     "generated at %s", paths.krb5conf)
 
-        if serverrole == "domain controller":
+        if serverrole == "active directory domain controller":
             create_dns_update_list(lp, logger, paths)
 
         backend_result = provision_backend.post_setup()
         provision_backend.shutdown()
 
-        create_phpldapadmin_config(paths.phpldapadminconfig,
-                                   ldapi_url)
     except:
         secrets_ldb.transaction_cancel()
         raise
@@ -1855,6 +2202,11 @@ def provision(logger, session_info, credentials, smbconf=None,
 
     result.backend_result = backend_result
 
+    if use_rfc2307:
+        provision_fake_ypserver(logger=logger, samdb=samdb,
+                domaindn=names.domaindn, netbiosname=names.netbiosname,
+                nisdomain=names.domain.lower(), maxuid=maxuid, maxgid=maxgid)
+
     return result
 
 
@@ -1863,9 +2215,9 @@ def provision_become_dc(smbconf=None, targetdir=None,
         serverdn=None, domain=None, hostname=None, domainsid=None,
         adminpass=None, krbtgtpass=None, domainguid=None, policyguid=None,
         policyguid_dc=None, invocationid=None, machinepass=None, dnspass=None,
-        dns_backend=None, root=None, nobody=None, users=None, wheel=None,
+        dns_backend=None, root=None, nobody=None, users=None,
         backup=None, serverrole=None, ldap_backend=None,
-        ldap_backend_type=None, sitename=None, debuglevel=1):
+        ldap_backend_type=None, sitename=None, debuglevel=1, use_ntvfs=False):
 
     logger = logging.getLogger("provision")
     samba.set_debug_level(debuglevel)
@@ -1875,21 +2227,14 @@ def provision_become_dc(smbconf=None, targetdir=None,
         realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
         configdn=configdn, serverdn=serverdn, domain=domain,
         hostname=hostname, hostip=None, domainsid=domainsid,
-        machinepass=machinepass, serverrole="domain controller",
-        sitename=sitename, dns_backend=dns_backend, dnspass=dnspass)
+        machinepass=machinepass,
+        serverrole="active directory domain controller",
+        sitename=sitename, dns_backend=dns_backend, dnspass=dnspass,
+        use_ntvfs=use_ntvfs)
     res.lp.set("debuglevel", str(debuglevel))
     return res
 
 
-def create_phpldapadmin_config(path, ldapi_uri):
-    """Create a PHP LDAP admin configuration file.
-
-    :param path: Path to write the configuration to.
-    """
-    setup_file(setup_path("phpldapadmin-config.php"), path,
-            {"S4_LDAPI_URI": ldapi_uri})
-
-
 def create_krb5_conf(path, dnsdomain, hostname, realm):
     """Write out a file containing zone statements suitable for inclusion in a
     named.conf file (including GSS-TSIG configuration).
@@ -1926,8 +2271,8 @@ class InvalidNetbiosName(Exception):
 
 class MissingShareError(ProvisioningError):
 
-    def __init__(self, name, smbconf, smbconf_template):
+    def __init__(self, name, smbconf):
         super(MissingShareError, self).__init__(
             "Existing smb.conf does not have a [%s] share, but you are "
-            "configuring a DC. Please either remove %s or see the template "
-            "at %s" % (name, smbconf, smbconf_template))
+            "configuring a DC. Please remove %s or add the share manually." %
+            (name, smbconf))