s4-provision Add initial support for joining as a new subdomain
[amitay/samba.git] / source4 / scripting / python / samba / provision / __init__.py
index e200083a333f1ab74f071cef6f1a2a62a31e519d..a3633ab1280d534df4c01fc63b6623d3965b925a 100644 (file)
@@ -38,22 +38,23 @@ import uuid
 import socket
 import urllib
 import shutil
+import string
 
 import ldb
 
 from samba.auth import system_session, admin_session
 import samba
+from samba.dsdb import DS_DOMAIN_FUNCTION_2000
 from samba import (
     Ldb,
     check_all_substituted,
-    in_source_tree,
     read_and_sub_file,
     setup_file,
     substitute_var,
     valid_netbios_name,
     version,
     )
-from samba.dcerpc import security
+from samba.dcerpc import security, misc
 from samba.dcerpc.misc import (
     SEC_CHAN_BDC,
     SEC_CHAN_WKSTA,
@@ -73,10 +74,14 @@ from samba.provision.backend import (
     LDBBackend,
     OpenLDAPBackend,
     )
+from samba.provision.sambadns import setup_ad_dns
+
 import samba.param
 import samba.registry
 from samba.schema import Schema
 from samba.samdb import SamDB
+from samba.dbchecker import dbcheck
+
 
 VALID_NETBIOS_CHARS = " !#$%&'()-.@^_{}~"
 DEFAULT_POLICY_GUID = "31B2F340-016D-11D2-945F-00C04FB984F9"
@@ -85,39 +90,14 @@ DEFAULTSITE = "Default-First-Site-Name"
 LAST_PROVISION_USN_ATTRIBUTE = "lastProvisionUSN"
 
 
-def find_setup_dir():
-    """Find the setup directory used by provision."""
-    if in_source_tree():
-        # In source tree
-        dirname = os.path.dirname(__file__)
-        return os.path.normpath(os.path.join(dirname, "../../../../setup"))
-    else:
-        import sys
-        for prefix in [sys.prefix,
-                os.path.join(os.path.dirname(__file__), "../../../../..")]:
-            for suffix in ["share/setup", "share/samba/setup", "setup"]:
-                ret = os.path.normpath(os.path.join(prefix, suffix))
-                if os.path.isdir(ret):
-                    return ret
-        raise Exception("Unable to find setup directory.")
+def setup_path(file):
+    """Return an absolute path to the provision tempate file specified by file"""
+    return os.path.join(samba.param.setup_dir(), file)
 
 # Descriptors of naming contexts and other important objects
 
 # "get_schema_descriptor" is located in "schema.py"
 
-def get_sites_descriptor(domain_sid):
-    sddl = "D:(A;;RPLCLORC;;;AU)" \
-           "(A;;RPWPCRCCLCLORCWOWDSW;;;EA)" \
-           "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \
-           "S:AI(AU;CISA;CCDCSDDT;;;WD)" \
-           "(OU;CIIOSA;CR;;f0f8ffab-1191-11d0-a060-00aa006c33ed;WD)" \
-           "(OU;CIIOSA;WP;f30e3bbe-9ff0-11d1-b603-0000f80367c1;bf967ab3-0de6-11d0-a285-00aa003049e2;WD)" \
-           "(OU;CIIOSA;WP;f30e3bbf-9ff0-11d1-b603-0000f80367c1;bf967ab3-0de6-11d0-a285-00aa003049e2;WD)" \
-           "(OU;CIIOSA;WP;3e10944c-c354-11d0-aff8-0000f80367c1;b7b13124-b82e-11d0-afee-0000f80367c1;WD)"
-    sec = security.descriptor.from_sddl(sddl, domain_sid)
-    return ndr_pack(sec)
-
-
 def get_config_descriptor(domain_sid):
     sddl = "O:EAG:EAD:(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
            "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
@@ -228,8 +208,112 @@ class ProvisionNames(object):
         self.sitename = None
         self.smbconf = None
 
+def find_provision_key_parameters(samdb, secretsdb, idmapdb, paths, smbconf, lp):
+    """Get key provision parameters (realm, domain, ...) from a given provision
 
-def update_provision_usn(samdb, low, high, replace=False):
+    :param samdb: An LDB object connected to the sam.ldb file
+    :param secretsdb: An LDB object connected to the secrets.ldb file
+    :param idmapdb: An LDB object connected to the idmap.ldb file
+    :param paths: A list of path to provision object
+    :param smbconf: Path to the smb.conf file
+    :param lp: A LoadParm object
+    :return: A list of key provision parameters
+    """
+    names = ProvisionNames()
+    names.adminpass = None
+
+    # NT domain, kerberos realm, root dn, domain dn, domain dns name
+    names.domain = string.upper(lp.get("workgroup"))
+    names.realm = lp.get("realm")
+    names.dnsdomain = names.realm.lower()
+    basedn = samba.dn_from_dns_name(names.dnsdomain)
+    names.realm = string.upper(names.realm)
+    # netbiosname
+    # Get the netbiosname first (could be obtained from smb.conf in theory)
+    res = secretsdb.search(expression="(flatname=%s)" %
+                            names.domain,base="CN=Primary Domains",
+                            scope=ldb.SCOPE_SUBTREE, attrs=["sAMAccountName"])
+    names.netbiosname = str(res[0]["sAMAccountName"]).replace("$","")
+
+    names.smbconf = smbconf
+
+    # That's a bit simplistic but it's ok as long as we have only 3
+    # partitions
+    current = samdb.search(expression="(objectClass=*)",
+        base="", scope=ldb.SCOPE_BASE,
+        attrs=["defaultNamingContext", "schemaNamingContext",
+               "configurationNamingContext","rootDomainNamingContext"])
+
+    names.configdn = current[0]["configurationNamingContext"]
+    configdn = str(names.configdn)
+    names.schemadn = current[0]["schemaNamingContext"]
+    if not (ldb.Dn(samdb, basedn) == (ldb.Dn(samdb,
+                                       current[0]["defaultNamingContext"][0]))):
+        raise ProvisioningError(("basedn in %s (%s) and from %s (%s)"
+                                 "is not the same ..." % (paths.samdb,
+                                    str(current[0]["defaultNamingContext"][0]),
+                                    paths.smbconf, basedn)))
+
+    names.domaindn=current[0]["defaultNamingContext"]
+    names.rootdn=current[0]["rootDomainNamingContext"]
+    # default site name
+    res3 = samdb.search(expression="(objectClass=site)",
+        base="CN=Sites," + configdn, scope=ldb.SCOPE_ONELEVEL, attrs=["cn"])
+    names.sitename = str(res3[0]["cn"])
+
+    # dns hostname and server dn
+    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,"")
+
+    server_res = samdb.search(expression="serverReference=%s" % res4[0].dn,
+                                attrs=[], base=configdn)
+    names.serverdn = server_res[0].dn
+
+    # invocation id/objectguid
+    res5 = samdb.search(expression="(objectClass=*)",
+            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]))
+
+    # domain guid/sid
+    res6 = samdb.search(expression="(objectClass=*)", base=basedn,
+            scope=ldb.SCOPE_BASE, attrs=["objectGUID",
+                "objectSid","msDS-Behavior-Version" ])
+    names.domainguid = str(ndr_unpack(misc.GUID, res6[0]["objectGUID"][0]))
+    names.domainsid = ndr_unpack( security.dom_sid, res6[0]["objectSid"][0])
+    if res6[0].get("msDS-Behavior-Version") is None or \
+        int(res6[0]["msDS-Behavior-Version"][0]) < DS_DOMAIN_FUNCTION_2000:
+        names.domainlevel = DS_DOMAIN_FUNCTION_2000
+    else:
+        names.domainlevel = int(res6[0]["msDS-Behavior-Version"][0])
+
+    # policy guid
+    res7 = samdb.search(expression="(displayName=Default Domain Policy)",
+                        base="CN=Policies,CN=System," + basedn,
+                        scope=ldb.SCOPE_ONELEVEL, attrs=["cn","displayName"])
+    names.policyid = str(res7[0]["cn"]).replace("{","").replace("}","")
+    # dc policy guid
+    res8 = samdb.search(expression="(displayName=Default Domain Controllers"
+                                   " Policy)",
+                            base="CN=Policies,CN=System," + basedn,
+                            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"]
+    else:
+        raise ProvisioningError("Unable to find uid/gid for Domain Admins rid")
+    return names
+
+def update_provision_usn(samdb, low, high, id, replace=False):
     """Update the field provisionUSN in sam.ldb
 
     This field is used to track range of USN modified by provision and
@@ -240,28 +324,35 @@ def update_provision_usn(samdb, low, high, replace=False):
     :param samdb: An LDB object connect to sam.ldb
     :param low: The lowest USN modified by this upgrade
     :param high: The highest USN modified by this upgrade
+    :param id: The invocation id of the samba's dc
     :param replace: A boolean indicating if the range should replace any
                     existing one or appended (default)
     """
 
     tab = []
     if not replace:
-        entry = samdb.search(expression="(&(dn=@PROVISION)(%s=*))" %
-                                LAST_PROVISION_USN_ATTRIBUTE, base="",
-                                scope=ldb.SCOPE_SUBTREE,
-                                attrs=[LAST_PROVISION_USN_ATTRIBUTE, "dn"])
+        entry = samdb.search(base="@PROVISION",
+                             scope=ldb.SCOPE_BASE,
+                             attrs=[LAST_PROVISION_USN_ATTRIBUTE, "dn"])
         for e in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
+            if not re.search(';', e):
+                e = "%s;%s" % (e, id)
             tab.append(str(e))
 
-    tab.append("%s-%s" % (low, high))
+    tab.append("%s-%s;%s" % (low, high, id))
     delta = ldb.Message()
     delta.dn = ldb.Dn(samdb, "@PROVISION")
     delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
         ldb.FLAG_MOD_REPLACE, LAST_PROVISION_USN_ATTRIBUTE)
+    entry = samdb.search(expression='provisionnerID=*',
+                         base="@PROVISION", scope=ldb.SCOPE_BASE,
+                         attrs=["provisionnerID"])
+    if len(entry) == 0 or len(entry[0]) == 0:
+        delta["provisionnerID"] = ldb.MessageElement(id, ldb.FLAG_MOD_ADD, "provisionnerID")
     samdb.modify(delta)
 
 
-def set_provision_usn(samdb, low, high):
+def set_provision_usn(samdb, low, high, id):
     """Set the field provisionUSN in sam.ldb
     This field is used to track range of USN modified by provision and
     upgradeprovision.
@@ -270,9 +361,12 @@ def set_provision_usn(samdb, low, high):
 
     :param samdb: An LDB object connect to sam.ldb
     :param low: The lowest USN modified by this upgrade
-    :param high: The highest USN modified by this upgrade"""
+    :param high: The highest USN modified by this upgrade
+    :param id: The invocationId of the provision"""
+
     tab = []
-    tab.append("%s-%s" % (low, high))
+    tab.append("%s-%s;%s" % (low, high, id))
+
     delta = ldb.Message()
     delta.dn = ldb.Dn(samdb, "@PROVISION")
     delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
@@ -297,25 +391,40 @@ def get_max_usn(samdb,basedn):
 
 
 def get_last_provision_usn(sam):
-    """Get the lastest USN modified by a provision or an upgradeprovision
+    """Get USNs ranges modified by a provision or an upgradeprovision
 
     :param sam: An LDB object pointing to the sam.ldb
-    :return: an integer corresponding to the highest USN modified by
-        (upgrade)provision, 0 is this value is unknown
+    :return: a dictionnary which keys are invocation id and values are an array
+             of integer representing the different ranges
     """
-    entry = sam.search(expression="(&(dn=@PROVISION)(%s=*))" %
-                        LAST_PROVISION_USN_ATTRIBUTE,
-                        base="", scope=ldb.SCOPE_SUBTREE,
-                        attrs=[LAST_PROVISION_USN_ATTRIBUTE])
+    try:
+        entry = sam.search(expression="%s=*" % LAST_PROVISION_USN_ATTRIBUTE,
+                           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):
-        range = []
-        idx = 0
+        myids = []
+        range = {}
         p = re.compile(r'-')
+        if entry[0].get("provisionnerID"):
+            for e in entry[0]["provisionnerID"]:
+                myids.append(str(e))
         for r in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
-            tab = p.split(str(r))
-            range.append(tab[0])
-            range.append(tab[1])
-            idx = idx + 1
+            tab1 = str(r).split(';')
+            if len(tab1) == 2:
+                id = tab1[1]
+            else:
+                id = "default"
+            if (len(myids) > 0 and id not in myids):
+                continue
+            tab2 = p.split(tab1[0])
+            if range.get(id) == None:
+                range[id] = []
+            range[id].append(tab2[0])
+            range[id].append(tab2[1])
         return range
     else:
         return None
@@ -328,6 +437,8 @@ class ProvisionResult(object):
         self.domaindn = None
         self.lp = None
         self.samdb = None
+        self.idmap = None
+        self.names = None
 
 
 def check_install(lp, session_info, credentials):
@@ -339,7 +450,7 @@ def check_install(lp, session_info, credentials):
     """
     if lp.get("realm") == "":
         raise Exception("Realm empty")
-    samdb = Ldb(lp.get("sam database"), session_info=session_info,
+    samdb = Ldb(lp.samdb_url(), session_info=session_info,
             credentials=credentials, lp=lp)
     if len(samdb.search("(cn=Administrator)")) != 1:
         raise ProvisioningError("No administrator account found")
@@ -424,12 +535,9 @@ def provision_paths_from_lp(lp, dnsdomain):
     paths.keytab = "secrets.keytab"
 
     paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
-    paths.samdb = os.path.join(paths.private_dir,
-        lp.get("sam database") or "samdb.ldb")
-    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.samdb = os.path.join(paths.private_dir, "sam.ldb")
+    paths.idmapdb = os.path.join(paths.private_dir, "idmap.ldb")
+    paths.secrets = os.path.join(paths.private_dir, "secrets.ldb")
     paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
     paths.dns = os.path.join(paths.private_dir, "dns", dnsdomain + ".zone")
     paths.dns_update_list = os.path.join(paths.private_dir, "dns_update_list")
@@ -500,7 +608,7 @@ def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
         raise ProvisioningError("guess_names: 'realm=%s' in %s must match chosen realm '%s'!  Please remove the smb.conf file and let provision generate it" % (lp.get("realm").upper(), realm, lp.configfile))
 
     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").upper(), serverrole, lp.configfile))
+        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"), serverrole, lp.configfile))
 
     if serverrole == "domain controller":
         if domain is None:
@@ -512,7 +620,7 @@ def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
             raise ProvisioningError("guess_names: Workgroup '%s' in smb.conf must match chosen domain '%s'!  Please remove the %s file and let provision generate it" % (lp.get("workgroup").upper(), domain, lp.configfile))
 
         if domaindn is None:
-            domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
+            domaindn = samba.dn_from_dns_name(dnsdomain)
 
         if domain == netbiosname:
             raise ProvisioningError("guess_names: Domain '%s' must not be equal to short host name '%s'!" % (domain, netbiosname))
@@ -560,7 +668,7 @@ def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
     return names
 
 
-def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
+def make_smbconf(smbconf, hostname, domain, realm, serverrole,
                  targetdir, sid_generator="internal", eadb=False, lp=None):
     """Create a new smb.conf file based on a couple of basic settings.
     """
@@ -613,25 +721,19 @@ def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
     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)
+        statedir_line = "state directory = " + os.path.abspath(targetdir)
+        cachedir_line = "cache directory = " + os.path.abspath(targetdir)
 
         lp.set("lock dir", os.path.abspath(targetdir))
+        lp.set("state directory", os.path.abspath(targetdir))
+        lp.set("cache directory", os.path.abspath(targetdir))
     else:
         privatedir_line = ""
         lockdir_line = ""
+        statedir_line = ""
+        cachedir_line = ""
 
-    if sid_generator == "internal":
-        sid_generator_line = ""
-    else:
-        sid_generator_line = "sid generator = " + sid_generator
-
-    used_setup_dir = setup_path("")
-    default_setup_dir = lp.get("setup directory")
-    setupdir_line = ""
-    if used_setup_dir != default_setup_dir:
-        setupdir_line = "setup directory = %s" % used_setup_dir
-        lp.set("setup directory", used_setup_dir)
-
-    sysvol = os.path.join(lp.get("lock dir"), "sysvol")
+    sysvol = os.path.join(lp.get("state directory"), "sysvol")
     netlogon = os.path.join(sysvol, realm.lower(), "scripts")
 
     setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
@@ -642,10 +744,10 @@ def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
             "SERVERROLE": serverrole,
             "NETLOGONPATH": netlogon,
             "SYSVOLPATH": sysvol,
-            "SETUPDIRECTORY_LINE": setupdir_line,
-            "SIDGENERATOR_LINE": sid_generator_line,
             "PRIVATEDIR_LINE": privatedir_line,
-            "LOCKDIR_LINE": lockdir_line
+            "LOCKDIR_LINE": lockdir_line,
+            "STATEDIR_LINE": statedir_line,
+            "CACHEDIR_LINE": cachedir_line
             })
 
     # reload the smb.conf
@@ -660,7 +762,7 @@ def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
 
 
 
-def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
+def setup_name_mappings(idmap, sid, root_uid, nobody_uid,
                         users_gid, wheel_gid):
     """setup reasonable name mappings for sam names to unix names.
 
@@ -680,7 +782,7 @@ def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
     idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
 
 
-def setup_samdb_partitions(samdb_path, setup_path, logger, lp, session_info,
+def setup_samdb_partitions(samdb_path, logger, lp, session_info,
                            provision_backend, names, schema, serverrole,
                            erase=False):
     """Setup the partitions for the SAM database.
@@ -715,10 +817,7 @@ def setup_samdb_partitions(samdb_path, setup_path, logger, lp, session_info,
     try:
         logger.info("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(),
-                "CONFIGDN": ldb.Dn(schema.ldb, names.configdn).get_casefold(),
-                "DOMAINDN": ldb.Dn(schema.ldb, names.domaindn).get_casefold(),
-                "LDAP_BACKEND_LINE": ldap_backend_line,
+                "LDAP_BACKEND_LINE": ldap_backend_line
         })
 
 
@@ -728,7 +827,7 @@ def setup_samdb_partitions(samdb_path, setup_path, logger, lp, session_info,
                 })
 
         logger.info("Setting up sam.ldb rootDSE")
-        setup_samdb_rootdse(samdb, setup_path, names)
+        setup_samdb_rootdse(samdb, names)
     except Exception:
         samdb.transaction_cancel()
         raise
@@ -823,12 +922,11 @@ def secretsdb_self_join(secretsdb, domain,
         secretsdb.add(msg)
 
 
-def secretsdb_setup_dns(secretsdb, setup_path, names, private_dir, realm,
+def secretsdb_setup_dns(secretsdb, names, private_dir, realm,
                         dnsdomain, dns_keytab_path, dnspass):
     """Add DNS specific bits to a secrets database.
 
     :param secretsdb: Ldb Handle to the secrets database
-    :param setup_path: Setup path function
     :param machinepass: Machine password
     """
     try:
@@ -847,14 +945,13 @@ def secretsdb_setup_dns(secretsdb, setup_path, names, private_dir, realm,
             })
 
 
-def setup_secretsdb(paths, setup_path, session_info, backend_credentials, lp):
+def setup_secretsdb(paths, session_info, backend_credentials, lp):
     """Setup the secrets database.
 
    :note: This function does not handle exceptions and transaction on purpose,
        it's up to the caller to do this job.
 
     :param path: Path to the secrets database.
-    :param setup_path: Get the path to a setup file.
     :param session_info: Session info.
     :param credentials: Credentials
     :param lp: Loadparm context
@@ -898,18 +995,17 @@ def setup_secretsdb(paths, setup_path, session_info, backend_credentials, lp):
                         "LDAPADMINREALM": backend_credentials.get_realm(),
                         "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
                         })
-
-        return secrets_ldb
     except Exception:
         secrets_ldb.transaction_cancel()
         raise
+    return secrets_ldb
 
 
-def setup_privileges(path, setup_path, session_info, lp):
+
+def setup_privileges(path, session_info, lp):
     """Setup the privileges database.
 
     :param path: Path to the privileges database.
-    :param setup_path: Get the path to a setup file.
     :param session_info: Session info.
     :param credentials: Credentials
     :param lp: Loadparm context
@@ -922,11 +1018,10 @@ def setup_privileges(path, setup_path, session_info, lp):
     privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
 
 
-def setup_registry(path, setup_path, session_info, lp):
+def setup_registry(path, session_info, lp):
     """Setup the registry.
 
     :param path: Path to the registry database
-    :param setup_path: Function that returns the path to a setup.
     :param session_info: Session information
     :param credentials: Credentials
     :param lp: Loadparm context
@@ -939,11 +1034,10 @@ def setup_registry(path, setup_path, session_info, lp):
     reg.diff_apply(provision_reg)
 
 
-def setup_idmapdb(path, setup_path, session_info, lp):
+def setup_idmapdb(path, session_info, lp):
     """Setup the idmap database.
 
     :param path: path to the idmap database
-    :param setup_path: Function that returns a path to a setup file
     :param session_info: Session information
     :param credentials: Credentials
     :param lp: Loadparm context
@@ -957,31 +1051,34 @@ def setup_idmapdb(path, setup_path, session_info, lp):
     return idmap_ldb
 
 
-def setup_samdb_rootdse(samdb, setup_path, names):
+def setup_samdb_rootdse(samdb, names):
     """Setup the SamDB rootdse.
 
     :param samdb: Sam Database handle
-    :param setup_path: Obtain setup path
     """
     setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
         "SCHEMADN": names.schemadn,
         "DOMAINDN": names.domaindn,
-        "ROOTDN": names.rootdn,
+        "ROOTDN"  : names.rootdn,
         "CONFIGDN": names.configdn,
         "SERVERDN": names.serverdn,
         })
 
 
-def setup_self_join(samdb, names, machinepass, dnspass,
-                    domainsid, next_rid, invocationid, setup_path,
+def setup_self_join(samdb, admin_session_info, names, fill, machinepass, dnspass,
+                    domainsid, next_rid, invocationid,
                     policyguid, policyguid_dc, domainControllerFunctionality,
-                    ntdsguid):
+                    ntdsguid, dc_rid=None):
     """Join a host to its own domain."""
     assert isinstance(invocationid, str)
     if ntdsguid is not None:
         ntdsguid_line = "objectGUID: %s\n"%ntdsguid
     else:
         ntdsguid_line = ""
+
+    if dc_rid is None:
+        dc_rid = next_rid
+
     setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
               "CONFIGDN": names.configdn,
               "SCHEMADN": names.schemadn,
@@ -992,11 +1089,13 @@ def setup_self_join(samdb, names, machinepass, dnspass,
               "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
               "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
               "DOMAINSID": str(domainsid),
-              "DCRID": str(next_rid),
+              "DCRID": str(dc_rid),
               "SAMBA_VERSION_STRING": version,
               "NTDSGUID": ntdsguid_line,
               "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
-                  domainControllerFunctionality)})
+                  domainControllerFunctionality),
+              "RIDALLOCATIONSTART": str(next_rid + 100),
+              "RIDALLOCATIONEND": str(next_rid + 100 + 499)})
 
     setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
               "POLICYGUID": policyguid,
@@ -1004,27 +1103,50 @@ def setup_self_join(samdb, names, machinepass, dnspass,
               "DNSDOMAIN": names.dnsdomain,
               "DOMAINDN": names.domaindn})
 
-    # add the NTDSGUID based SPNs
-    ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
-    names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID",
-                                     expression="", scope=ldb.SCOPE_BASE)
-    assert isinstance(names.ntdsguid, str)
+    # 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:
+        setup_add_ldif(samdb, setup_path("provision_self_join_config.ldif"), {
+                "CONFIGDN": names.configdn,
+                "SCHEMADN": names.schemadn,
+                "DOMAINDN": names.domaindn,
+                "SERVERDN": names.serverdn,
+                "INVOCATIONID": invocationid,
+                "NETBIOSNAME": names.netbiosname,
+                "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
+                "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
+                "DOMAINSID": str(domainsid),
+                "DCRID": str(dc_rid),
+                "SAMBA_VERSION_STRING": version,
+                "NTDSGUID": ntdsguid_line,
+                "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
+                    domainControllerFunctionality)})
+
+    # Setup fSMORoleOwner entries to point at the newly created DC entry
+        setup_modify_ldif(samdb, setup_path("provision_self_join_modify_config.ldif"), {
+                "CONFIGDN": names.configdn,
+                "SCHEMADN": names.schemadn,
+                "DEFAULTSITE": names.sitename,
+                "NETBIOSNAME": names.netbiosname,
+                "SERVERDN": names.serverdn,
+                })
 
+    system_session_info = system_session()
+    samdb.set_session_info(system_session_info)
     # Setup fSMORoleOwner entries to point at the newly created DC entry
+
+    # to modify a serverReference under cn=config when we are a subdomain, we must
+    # be system due to ACLs
     setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
               "DOMAINDN": names.domaindn,
-              "CONFIGDN": names.configdn,
-              "SCHEMADN": names.schemadn,
-              "DEFAULTSITE": names.sitename,
               "SERVERDN": names.serverdn,
               "NETBIOSNAME": names.netbiosname,
-              "RIDALLOCATIONSTART": str(next_rid + 100),
-              "RIDALLOCATIONEND": str(next_rid + 100 + 499),
               })
 
-    # This is partially Samba4 specific and should be replaced by the correct
+    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.ldif"), {
+    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')),
@@ -1077,45 +1199,21 @@ def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
     create_gpo_struct(policy_path)
 
 
-def setup_samdb(path, setup_path, session_info, provision_backend, 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=1000):
+def setup_samdb(path, session_info, provision_backend, lp, names,
+        logger, fill, serverrole,
+        am_rodc=False, schema=None):
     """Setup a complete SAM Database.
 
     :note: This will wipe the main SAM database file!
     """
 
-    # Provision does not make much sense values larger than 1000000000
-    # as the upper range of the rIDAvailablePool is 1073741823 and
-    # we don't want to create a domain that cannot allocate rids.
-    if next_rid < 1000 or next_rid > 1000000000:
-        error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
-        error += "the valid range is %u-%u. The default is %u." % (
-            1000, 1000000000, 1000)
-        raise ProvisioningError(error)
-
-    # ATTENTION: Do NOT change these default values without discussion with the
-    # team and/or release manager. They have a big impact on the whole program!
-    domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
-
-    if dom_for_fun_level is None:
-        dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
-
-    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_R2). This won't work!")
-
-    domainFunctionality = dom_for_fun_level
-    forestFunctionality = dom_for_fun_level
-
     # Also wipes the database
-    setup_samdb_partitions(path, setup_path, logger=logger, lp=lp,
+    setup_samdb_partitions(path, logger=logger, lp=lp,
         provision_backend=provision_backend, session_info=session_info,
         names=names, serverrole=serverrole, schema=schema)
 
     if schema is None:
-        schema = Schema(setup_path, domainsid, schemadn=names.schemadn)
+        schema = Schema(domainsid, schemadn=names.schemadn)
 
     # Load the database, but don's load the global schema and don't connect
     # quite yet
@@ -1136,8 +1234,42 @@ def setup_samdb(path, setup_path, session_info, provision_backend, lp, names,
     # DB
     samdb.connect(path)
 
-    if fill == FILL_DRS:
-        return samdb
+    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):
+
+    if next_rid is None:
+        next_rid = 1000
+
+    # Provision does not make much sense values larger than 1000000000
+    # as the upper range of the rIDAvailablePool is 1073741823 and
+    # we don't want to create a domain that cannot allocate rids.
+    if next_rid < 1000 or next_rid > 1000000000:
+        error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
+        error += "the valid range is %u-%u. The default is %u." % (
+            1000, 1000000000, 1000)
+        raise ProvisioningError(error)
+
+    # ATTENTION: Do NOT change these default values without discussion with the
+    # team and/or release manager. They have a big impact on the whole program!
+    domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
+
+    if dom_for_fun_level is None:
+        dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
+
+    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_R2). This won't work!")
+
+    domainFunctionality = dom_for_fun_level
+    forestFunctionality = dom_for_fun_level
+
+    # Set the NTDS settings DN manually - in order to have it already around
+    # before the provisioned tree exists and we connect
+    samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
 
     samdb.transaction_start()
     try:
@@ -1175,7 +1307,7 @@ def setup_samdb(path, setup_path, session_info, provision_backend, lp, names,
 
         setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
             "DOMAINDN": names.domaindn,
-            "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
+            "CREATTIME": str(samba.unix2nttime(int(time.time()))),
             "NEXTRID": str(next_rid),
             "DEFAULTSITE": names.sitename,
             "CONFIGDN": names.configdn,
@@ -1184,65 +1316,62 @@ def setup_samdb(path, setup_path, session_info, provision_backend, lp, names,
             "SAMBA_VERSION_STRING": version
             })
 
-        logger.info("Adding configuration container")
-        descr = b64encode(get_config_descriptor(domainsid))
-        setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
-            "CONFIGDN": names.configdn,
-            "DESCRIPTOR": descr,
-            })
-
-        # The LDIF here was created when the Schema object was constructed
-        logger.info("Setting up sam.ldb schema")
-        samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
-        samdb.modify_ldif(schema.schema_dn_modify)
-        samdb.write_prefixes_from_schema()
-        samdb.add_ldif(schema.schema_data, controls=["relax:0"])
-        setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
-                       {"SCHEMADN": names.schemadn})
+        # 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("Adding configuration container")
+            descr = b64encode(get_config_descriptor(domainsid))
+            setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
+                    "CONFIGDN": names.configdn,
+                    "DESCRIPTOR": descr,
+                    })
+
+            # The LDIF here was created when the Schema object was constructed
+            logger.info("Setting up sam.ldb schema")
+            samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
+            samdb.modify_ldif(schema.schema_dn_modify)
+            samdb.write_prefixes_from_schema()
+            samdb.add_ldif(schema.schema_data, controls=["relax:0"])
+            setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
+                           {"SCHEMADN": names.schemadn})
+
+        # Now register this container in the root of the forest
+        msg = ldb.Message(ldb.Dn(samdb, names.domaindn))
+        msg["subRefs"] = ldb.MessageElement(names.configdn , ldb.FLAG_MOD_ADD,
+                    "subRefs")
 
-        logger.info("Reopening sam.ldb with new schema")
     except Exception:
         samdb.transaction_cancel()
         raise
     else:
         samdb.transaction_commit()
 
-    samdb = SamDB(session_info=admin_session_info, auto_connect=False,
-                credentials=provision_backend.credentials, lp=lp,
-                global_schema=False, am_rodc=am_rodc)
-
-    # Set the NTDS settings DN manually - in order to have it already around
-    # before the provisioned tree exists and we connect
-    samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
-    samdb.connect(path)
-
     samdb.transaction_start()
     try:
         samdb.invocation_id = invocationid
 
-        logger.info("Setting up sam.ldb configuration data")
-        descr = b64encode(get_sites_descriptor(domainsid))
-        setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
-            "CONFIGDN": names.configdn,
-            "NETBIOSNAME": names.netbiosname,
-            "DEFAULTSITE": names.sitename,
-            "DNSDOMAIN": names.dnsdomain,
-            "DOMAIN": names.domain,
-            "SCHEMADN": names.schemadn,
-            "DOMAINDN": names.domaindn,
-            "SERVERDN": names.serverdn,
-            "FOREST_FUNCTIONALITY": str(forestFunctionality),
-            "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
-            "SITES_DESCRIPTOR": descr
-            })
-
-        logger.info("Setting up display specifiers")
-        display_specifiers_ldif = read_ms_ldif(
-            setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
-        display_specifiers_ldif = substitute_var(display_specifiers_ldif,
-            {"CONFIGDN": names.configdn})
-        check_all_substituted(display_specifiers_ldif)
-        samdb.add_ldif(display_specifiers_ldif)
+        # 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")
+            setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
+                    "CONFIGDN": names.configdn,
+                    "NETBIOSNAME": names.netbiosname,
+                    "DEFAULTSITE": names.sitename,
+                    "DNSDOMAIN": names.dnsdomain,
+                    "DOMAIN": names.domain,
+                    "SCHEMADN": names.schemadn,
+                    "DOMAINDN": names.domaindn,
+                    "SERVERDN": names.serverdn,
+                    "FOREST_FUNCTIONALITY": str(forestFunctionality),
+                    "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
+                    })
+
+            logger.info("Setting up display specifiers")
+            display_specifiers_ldif = read_ms_ldif(
+                setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
+            display_specifiers_ldif = substitute_var(display_specifiers_ldif,
+                                                     {"CONFIGDN": names.configdn})
+            check_all_substituted(display_specifiers_ldif)
+            samdb.add_ldif(display_specifiers_ldif)
 
         logger.info("Adding users container")
         setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
@@ -1259,7 +1388,7 @@ def setup_samdb(path, setup_path, session_info, provision_backend, lp, names,
                 "DOMAINDN": names.domaindn})
         logger.info("Setting up sam.ldb data")
         setup_add_ldif(samdb, setup_path("provision.ldif"), {
-            "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
+            "CREATTIME": str(samba.unix2nttime(int(time.time()))),
             "DOMAINDN": names.domaindn,
             "NETBIOSNAME": names.netbiosname,
             "DEFAULTSITE": names.sitename,
@@ -1269,35 +1398,42 @@ def setup_samdb(path, setup_path, session_info, provision_backend, lp, names,
             "POLICYGUID_DC": policyguid_dc
             })
 
-        setup_modify_ldif(samdb,
-            setup_path("provision_basedn_references.ldif"), {
-                "DOMAINDN": names.domaindn})
+        # 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:
+            setup_modify_ldif(samdb,
+                              setup_path("provision_configuration_references.ldif"), {
+                    "CONFIGDN": names.configdn,
+                    "SCHEMADN": names.schemadn})
 
-        setup_modify_ldif(samdb,
-            setup_path("provision_configuration_references.ldif"), {
+            logger.info("Setting up well known security principals")
+            setup_add_ldif(samdb, setup_path("provision_well_known_sec_princ.ldif"), {
                 "CONFIGDN": names.configdn,
-                "SCHEMADN": names.schemadn})
-        if fill == FILL_FULL:
+                })
+
+        if fill == FILL_FULL or fill == FILL_SUBDOMAIN:
+            setup_modify_ldif(samdb,
+                              setup_path("provision_basedn_references.ldif"),
+                              {"DOMAINDN": names.domaindn})
+
             logger.info("Setting up sam.ldb users and groups")
             setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
                 "DOMAINDN": names.domaindn,
                 "DOMAINSID": str(domainsid),
-                "CONFIGDN": names.configdn,
                 "ADMINPASS_B64": b64encode(adminpass.encode('utf-16-le')),
                 "KRBTGTPASS_B64": b64encode(krbtgtpass.encode('utf-16-le'))
                 })
 
             logger.info("Setting up self join")
-            setup_self_join(samdb, names=names, invocationid=invocationid,
-                dnspass=dnspass,
-                machinepass=machinepass,
-                domainsid=domainsid,
-                next_rid=next_rid,
-                policyguid=policyguid,
-                policyguid_dc=policyguid_dc,
-                setup_path=setup_path,
-                domainControllerFunctionality=domainControllerFunctionality,
-                ntdsguid=ntdsguid)
+            setup_self_join(samdb, admin_session_info, names=names, fill=fill, invocationid=invocationid,
+                            dnspass=dnspass,
+                            machinepass=machinepass,
+                            domainsid=domainsid,
+                            next_rid=next_rid,
+                            dc_rid=dc_rid,
+                            policyguid=policyguid,
+                            policyguid_dc=policyguid_dc,
+                            domainControllerFunctionality=domainControllerFunctionality,
+                            ntdsguid=ntdsguid)
 
             ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
             names.ntdsguid = samdb.searchone(basedn=ntds_dn,
@@ -1312,6 +1448,7 @@ def setup_samdb(path, setup_path, session_info, provision_backend, lp, names,
 
 
 FILL_FULL = "FULL"
+FILL_SUBDOMAIN = "SUBDOMAIN"
 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)"
@@ -1391,32 +1528,35 @@ def setsysvolacl(samdb, netlogon, sysvol, gid, domainsid, dnsdomain, domaindn,
     set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp)
 
 
-def provision(setup_dir, 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, adminpass=None, ldapadminpass=None, krbtgtpass=None,
-        domainguid=None, policyguid=None, policyguid_dc=None,
-        invocationid=None, machinepass=None, ntdsguid=None, dnspass=None,
-        root=None, nobody=None, users=None, wheel=None, backup=None, aci=None,
-        serverrole=None, dom_for_fun_level=None, ldap_backend_extra_port=None,
-        ldap_backend_forced_uri=None, backend_type=None, sitename=None,
-        ol_mmr_urls=None, ol_olc=None, setup_ds_path=None, slapd_path=None,
-        nosync=False, ldap_dryrun_mode=False, useeadb=False, am_rodc=False,
-        lp=None):
-    """Provision samba4
-
-    :note: caution, this wipes all existing data!
-    """
-
-    def setup_path(file):
-        return os.path.join(setup_dir, file)
-
-    if domainsid is None:
-        domainsid = security.random_sid()
-    else:
-        domainsid = security.dom_sid(domainsid)
-
+def interface_ips_v4(lp):
+    '''return only IPv4 IPs'''
+    ips = samba.interface_ips(lp, False)
+    ret = []
+    for i in ips:
+        if i.find(':') == -1:
+            ret.append(i)
+    return ret
+
+def interface_ips_v6(lp, linklocal=False):
+    '''return only IPv6 IPs'''
+    ips = samba.interface_ips(lp, False)
+    ret = []
+    for i in ips:
+        if i.find(':') != -1 and (linklocal or i.find('%') == -1):
+            ret.append(i)
+    return ret
+
+
+def provision_fill(samdb, secrets_ldb, logger, names, paths,
+                   domainsid, schema=None,
+                   targetdir=None, samdb_fill=FILL_FULL,
+                   hostip=None, hostip6=None,
+                   next_rid=1000, dc_rid=None, adminpass=None, krbtgtpass=None,
+                   domainguid=None, policyguid=None, policyguid_dc=None,
+                   invocationid=None, machinepass=None, ntdsguid=None,
+                   dns_backend=None, dnspass=None,
+                   serverrole=None, dom_for_fun_level=None,
+                   am_rodc=False, lp=None):
     # create/adapt the group policy GUIDs
     # Default GUID for default policy are described at
     # "How Core Group Policy Works"
@@ -1428,6 +1568,9 @@ def provision(setup_dir, logger, session_info, credentials, smbconf=None,
         policyguid_dc = DEFAULT_DC_POLICY_GUID
     policyguid_dc = policyguid_dc.upper()
 
+    if invocationid is None:
+        invocationid = str(uuid.uuid4())
+
     if adminpass is None:
         adminpass = samba.generate_random_password(12, 32)
     if krbtgtpass is None:
@@ -1436,6 +1579,134 @@ def provision(setup_dir, logger, session_info, credentials, smbconf=None,
         machinepass  = samba.generate_random_password(128, 255)
     if dnspass is None:
         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":
+        # 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)
+
+        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)
+
+        # Now set up the right msDS-SupportedEncryptionTypes into the DB
+        # In future, this might be determined from some configuration
+        kerberos_enctypes = str(ENC_ALL_TYPES)
+
+        try:
+            msg = ldb.Message(ldb.Dn(samdb,
+                                     samdb.searchone("distinguishedName",
+                                                     expression="samAccountName=%s$" % names.netbiosname,
+                                                     scope=ldb.SCOPE_SUBTREE)))
+            msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(
+                elements=kerberos_enctypes, flags=ldb.FLAG_MOD_REPLACE,
+                name="msDS-SupportedEncryptionTypes")
+            samdb.modify(msg)
+        except ldb.LdbError, (enum, estr):
+            if enum != ldb.ERR_NO_SUCH_ATTRIBUTE:
+                # It might be that this attribute does not exist in this schema
+                raise
+
+        secretsdb_setup_dns(secrets_ldb, names,
+                            paths.private_dir, realm=names.realm,
+                            dnsdomain=names.dnsdomain,
+                            dns_keytab_path=paths.dns_keytab, dnspass=dnspass)
+
+        # Default DNS backend is BIND9 using txt files for zone information
+        if not dns_backend:
+            dns_backend = "BIND9"
+
+        setup_ad_dns(samdb, names, logger, hostip=hostip, hostip6=hostip6,
+                     dns_backend=dns_backend, os_level=dom_for_fun_level)
+
+        domainguid = samdb.searchone(basedn=samdb.get_default_basedn(),
+                                     attribute="objectGUID")
+        assert isinstance(domainguid, str)
+
+        create_dns_dir(logger, paths)
+
+        # Only make a zone file on the first DC, it should be
+        # replicated with DNS replication
+        if dns_backend == "BIND9":
+            create_zone_file(lp, logger, paths, targetdir,
+                             dnsdomain=names.dnsdomain, hostip=hostip, hostip6=hostip6,
+                             hostname=names.hostname, realm=names.realm,
+                             domainguid=domainguid, ntdsguid=names.ntdsguid)
+
+        create_named_conf(paths, realm=names.realm,
+                          dnsdomain=names.dnsdomain, dns_backend=dns_backend)
+
+        create_named_txt(paths.namedtxt,
+                         realm=names.realm, dnsdomain=names.dnsdomain,
+                         dnsname = "%s.%s" % (names.hostname, names.dnsdomain),
+                         private_dir=paths.private_dir,
+                         keytab_name=paths.dns_keytab)
+        logger.info("See %s for an example configuration include file for BIND", paths.namedconf)
+        logger.info("and %s for further documentation required for secure DNS "
+                    "updates", paths.namedtxt)
+
+    lastProvisionUSNs = get_last_provision_usn(samdb)
+    maxUSN = get_max_usn(samdb, str(names.rootdn))
+    if lastProvisionUSNs is not None:
+        update_provision_usn(samdb, 0, maxUSN, invocationid, 1)
+    else:
+        set_provision_usn(samdb, 0, maxUSN, invocationid)
+
+    logger.info("Setting up sam.ldb rootDSE marking as synchronized")
+    setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"),
+                      { 'NTDSGUID' : names.ntdsguid })
+
+    # fix any dangling GUIDs from the provision
+    logger.info("Fixing provision GUIDs")
+    chk = dbcheck(samdb, samdb_schema=samdb,  verbose=False, fix=True, yes=True, quiet=True)
+    samdb.transaction_start()
+    # a small number of GUIDs are missing because of ordering issues in the
+    # provision code
+    for schema_obj in ['CN=Domain', 'CN=Organizational-Person', 'CN=Contact', 'CN=inetOrgPerson']:
+        chk.check_database(DN="%s,%s" % (schema_obj, names.schemadn),
+                           scope=ldb.SCOPE_BASE, attrs=['defaultObjectCategory'])
+    chk.check_database(DN="CN=IP Security,CN=System,%s" % names.domaindn,
+                       scope=ldb.SCOPE_ONELEVEL,
+                       attrs=['ipsecOwnersReference',
+                              'ipsecFilterReference',
+                              'ipsecISAKMPReference',
+                              'ipsecNegotiationPolicyReference',
+                              'ipsecNFAReference'])
+    samdb.transaction_commit()
+
+
+def provision(logger, session_info, credentials, smbconf=None,
+        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,
+        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, ldap_backend_extra_port=None,
+        ldap_backend_forced_uri=None, backend_type=None, sitename=None,
+        ol_mmr_urls=None, ol_olc=None, setup_ds_path=None, slapd_path=None,
+        nosync=False, ldap_dryrun_mode=False, useeadb=False, am_rodc=False,
+        lp=None):
+    """Provision samba4
+
+    :note: caution, this wipes all existing data!
+    """
+
     if ldapadminpass is None:
         # Make a new, random password between Samba and it's LDAP server
         ldapadminpass=samba.generate_random_password(128, 255)
@@ -1443,6 +1714,11 @@ def provision(setup_dir, logger, session_info, credentials, smbconf=None,
     if backend_type is None:
         backend_type = "ldb"
 
+    if domainsid is None:
+        domainsid = security.random_sid()
+    else:
+        domainsid = security.dom_sid(domainsid)
+
     sid_generator = "internal"
     if backend_type == "fedora-ds":
         sid_generator = "backend"
@@ -1474,11 +1750,11 @@ def provision(setup_dir, logger, session_info, credentials, smbconf=None,
         data = open(smbconf, 'r').read()
         data = data.lstrip()
         if data is None or data == "":
-            make_smbconf(smbconf, setup_path, hostname, domain, realm,
+            make_smbconf(smbconf, hostname, domain, realm,
                          serverrole, targetdir, sid_generator, useeadb,
                          lp=lp)
     else:
-        make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
+        make_smbconf(smbconf, hostname, domain, realm, serverrole,
                      targetdir, sid_generator, useeadb, lp=lp)
 
     if lp is None:
@@ -1487,29 +1763,42 @@ def provision(setup_dir, logger, session_info, credentials, smbconf=None,
     names = guess_names(lp=lp, hostname=hostname, domain=domain,
         dnsdomain=realm, serverrole=serverrole, domaindn=domaindn,
         configdn=configdn, schemadn=schemadn, serverdn=serverdn,
-        sitename=sitename)
+        sitename=sitename, rootdn=rootdn)
     paths = provision_paths_from_lp(lp, names.dnsdomain)
 
     paths.bind_gid = bind_gid
+    paths.wheel_gid = wheel_gid
 
     if hostip is None:
         logger.info("Looking up IPv4 addresses")
-        hostips = samba.interface_ips(lp, False)
-        if len(hostips) == 0:
-            logger.warning("No external IPv4 address has been found. Using loopback.")
-            hostip = '127.0.0.1'
-        else:
+        hostips = interface_ips_v4(lp)
+        if len(hostips) > 0:
             hostip = hostips[0]
             if len(hostips) > 1:
-                logger.warning("More than one IPv4 address found. Using %s.",
+                logger.warning("More than one IPv4 address found. Using %s",
                     hostip)
+    if hostip == "127.0.0.1":
+        hostip = None
+    if hostip is None:
+        logger.warning("No IPv4 address will be assigned")
+
+    if hostip6 is None:
+        logger.info("Looking up IPv6 addresses")
+        hostips = interface_ips_v6(lp, linklocal=False)
+        if hostips:
+            hostip6 = hostips[0]
+        if len(hostips) > 1:
+            logger.warning("More than one IPv6 address found. Using %s", hostip6)
+    if hostip6 is None:
+        logger.warning("No IPv6 address will be assigned")
+
+    names.hostip = hostip
+    names.hostip6 = hostip6
 
     if serverrole is None:
         serverrole = lp.get("server role")
 
     assert serverrole in ("domain controller", "member server", "standalone")
-    if invocationid is None:
-        invocationid = str(uuid.uuid4())
 
     if not os.path.exists(paths.private_dir):
         os.mkdir(paths.private_dir)
@@ -1518,21 +1807,21 @@ def provision(setup_dir, logger, session_info, credentials, smbconf=None,
 
     ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
 
-    schema = Schema(setup_path, domainsid, invocationid=invocationid,
+    schema = Schema(domainsid, invocationid=invocationid,
         schemadn=names.schemadn)
 
     if backend_type == "ldb":
         provision_backend = LDBBackend(backend_type, paths=paths,
-            setup_path=setup_path, lp=lp, credentials=credentials,
+            lp=lp, credentials=credentials,
             names=names, logger=logger)
     elif backend_type == "existing":
         provision_backend = ExistingBackend(backend_type, paths=paths,
-            setup_path=setup_path, lp=lp, credentials=credentials,
+            lp=lp, credentials=credentials,
             names=names, logger=logger,
             ldap_backend_forced_uri=ldap_backend_forced_uri)
     elif backend_type == "fedora-ds":
         provision_backend = FDSBackend(backend_type, paths=paths,
-            setup_path=setup_path, lp=lp, credentials=credentials,
+            lp=lp, credentials=credentials,
             names=names, logger=logger, domainsid=domainsid,
             schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
             slapd_path=slapd_path,
@@ -1542,7 +1831,7 @@ def provision(setup_dir, logger, session_info, credentials, smbconf=None,
             ldap_backend_forced_uri=ldap_backend_forced_uri)
     elif backend_type == "openldap":
         provision_backend = OpenLDAPBackend(backend_type, paths=paths,
-            setup_path=setup_path, lp=lp, credentials=credentials,
+            lp=lp, credentials=credentials,
             names=names, logger=logger, domainsid=domainsid,
             schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
             slapd_path=slapd_path,
@@ -1564,32 +1853,31 @@ def provision(setup_dir, logger, session_info, credentials, smbconf=None,
         share_ldb.load_ldif_file_add(setup_path("share.ldif"))
 
     logger.info("Setting up secrets.ldb")
-    secrets_ldb = setup_secretsdb(paths, setup_path,
+    secrets_ldb = setup_secretsdb(paths,
         session_info=session_info,
         backend_credentials=provision_backend.secrets_credentials, lp=lp)
 
     try:
         logger.info("Setting up the registry")
-        setup_registry(paths.hklm, setup_path, session_info,
+        setup_registry(paths.hklm, session_info,
                        lp=lp)
 
         logger.info("Setting up the privileges database")
-        setup_privileges(paths.privilege, setup_path, session_info, lp=lp)
+        setup_privileges(paths.privilege, session_info, lp=lp)
 
         logger.info("Setting up idmap db")
-        idmap = setup_idmapdb(paths.idmapdb, setup_path,
+        idmap = setup_idmapdb(paths.idmapdb,
             session_info=session_info, lp=lp)
 
+        setup_name_mappings(idmap, sid=str(domainsid),
+                            root_uid=root_uid, nobody_uid=nobody_uid,
+                            users_gid=users_gid, wheel_gid=wheel_gid)
+
         logger.info("Setting up SAM db")
-        samdb = setup_samdb(paths.samdb, setup_path, session_info,
-            provision_backend, 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)
+        samdb = setup_samdb(paths.samdb, session_info,
+                            provision_backend, lp, names, logger=logger,
+                            serverrole=serverrole,
+                            schema=schema, fill=samdb_fill, am_rodc=am_rodc)
 
         if serverrole == "domain controller":
             if paths.netlogon is None:
@@ -1609,92 +1897,30 @@ def provision(setup_dir, logger, session_info, credentials, smbconf=None,
                 os.makedirs(paths.netlogon, 0755)
 
         if samdb_fill == FILL_FULL:
-            setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
-                                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)
-                create_default_gpo(paths.sysvol, names.dnsdomain, policyguid,
-                    policyguid_dc)
-                setsysvolacl(samdb, paths.netlogon, paths.sysvol, wheel_gid,
-                    domainsid, names.dnsdomain, names.domaindn, lp)
-
-            logger.info("Setting up sam.ldb rootDSE marking as synchronized")
-            setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
-
-            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)
-
-            # Now set up the right msDS-SupportedEncryptionTypes into the DB
-            # In future, this might be determined from some configuration
-            kerberos_enctypes = str(ENC_ALL_TYPES)
-
-            try:
-                msg = ldb.Message(ldb.Dn(samdb,
-                    samdb.searchone("distinguishedName",
-                        expression="samAccountName=%s$" % names.netbiosname,
-                        scope=ldb.SCOPE_SUBTREE)))
-                msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(
-                    elements=kerberos_enctypes, flags=ldb.FLAG_MOD_REPLACE,
-                    name="msDS-SupportedEncryptionTypes")
-                samdb.modify(msg)
-            except ldb.LdbError, (enum, estr):
-                if enum != ldb.ERR_NO_SUCH_ATTRIBUTE:
-                    # It might be that this attribute does not exist in this schema
-                    raise
-
-            if serverrole == "domain controller":
-                secretsdb_setup_dns(secrets_ldb, setup_path, names,
-                    paths.private_dir, realm=names.realm,
-                    dnsdomain=names.dnsdomain,
-                    dns_keytab_path=paths.dns_keytab, dnspass=dnspass)
-
-                domainguid = samdb.searchone(basedn=domaindn,
-                    attribute="objectGUID")
-                assert isinstance(domainguid, str)
-
-                # Only make a zone file on the first DC, it should be
-                # replicated with DNS replication
-                create_zone_file(lp, logger, paths, targetdir, setup_path,
-                    dnsdomain=names.dnsdomain, hostip=hostip, hostip6=hostip6,
-                    hostname=names.hostname, realm=names.realm,
-                    domainguid=domainguid, ntdsguid=names.ntdsguid)
-
-                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, dnsdomain=names.dnsdomain,
-                    private_dir=paths.private_dir,
-                    keytab_name=paths.dns_keytab)
-                logger.info("See %s for an example configuration include file for BIND", paths.namedconf)
-                logger.info("and %s for further documentation required for secure DNS "
-                        "updates", paths.namedtxt)
-
-            lastProvisionUSNs = get_last_provision_usn(samdb)
-            maxUSN = get_max_usn(samdb, str(names.rootdn))
-            if lastProvisionUSNs is not None:
-                update_provision_usn(samdb, 0, maxUSN, 1)
-            else:
-                set_provision_usn(samdb, 0, maxUSN)
-
-        create_krb5_conf(paths.krb5conf, setup_path,
+            provision_fill(samdb, secrets_ldb, logger,
+                           names, paths, schema=schema, targetdir=targetdir,
+                           samdb_fill=samdb_fill, hostip=hostip, hostip6=hostip6, domainsid=domainsid,
+                           next_rid=next_rid, dc_rid=dc_rid, adminpass=adminpass,
+                           krbtgtpass=krbtgtpass, domainguid=domainguid,
+                           policyguid=policyguid, policyguid_dc=policyguid_dc,
+                           invocationid=invocationid, machinepass=machinepass,
+                           ntdsguid=ntdsguid, dns_backend=dns_backend, dnspass=dnspass,
+                           serverrole=serverrole, dom_for_fun_level=dom_for_fun_level,
+                           am_rodc=am_rodc, lp=lp)
+
+        create_krb5_conf(paths.krb5conf,
                          dnsdomain=names.dnsdomain, hostname=names.hostname,
                          realm=names.realm)
         logger.info("A Kerberos configuration suitable for Samba 4 has been "
                     "generated at %s", paths.krb5conf)
 
         if serverrole == "domain controller":
-            create_dns_update_list(lp, logger, paths, setup_path)
+            create_dns_update_list(lp, logger, paths)
 
         provision_backend.post_setup()
         provision_backend.shutdown()
 
-        create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
+        create_phpldapadmin_config(paths.phpldapadminconfig,
                                    ldapi_url)
     except Exception:
         secrets_ldb.transaction_cancel()
@@ -1714,7 +1940,6 @@ def provision(setup_dir, logger, session_info, credentials, smbconf=None,
                 logger.info("Failed to chown %s to bind gid %u",
                             dns_keytab_path, paths.bind_gid)
 
-
     logger.info("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php",
             paths.phpldapadminconfig)
 
@@ -1748,51 +1973,77 @@ def provision(setup_dir, logger, session_info, credentials, smbconf=None,
     result = ProvisionResult()
     result.domaindn = domaindn
     result.paths = paths
+    result.names = names
     result.lp = lp
     result.samdb = samdb
+    result.idmap = idmap
     return result
 
 
-def provision_become_dc(setup_dir=None, smbconf=None, targetdir=None,
+def provision_become_dc(smbconf=None, targetdir=None,
         realm=None, rootdn=None, domaindn=None, schemadn=None, configdn=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,
-        root=None, nobody=None, users=None, wheel=None, backup=None,
+        dns_backend=None, root=None, nobody=None, users=None, wheel=None, backup=None,
         serverrole=None, ldap_backend=None, ldap_backend_type=None,
         sitename=None, debuglevel=1):
 
     logger = logging.getLogger("provision")
     samba.set_debug_level(debuglevel)
 
-    res = provision(setup_dir, logger, system_session(), None,
+    res = provision(logger, system_session(), None,
         smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
         realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
         configdn=configdn, serverdn=serverdn, domain=domain,
-        hostname=hostname, hostip="127.0.0.1", domainsid=domainsid,
+        hostname=hostname, hostip=None, domainsid=domainsid,
         machinepass=machinepass, serverrole="domain controller",
-        sitename=sitename)
+        sitename=sitename, dns_backend=dns_backend, dnspass=dnspass)
     res.lp.set("debuglevel", str(debuglevel))
     return res
 
 
-def create_phpldapadmin_config(path, setup_path, ldapi_uri):
+def create_phpldapadmin_config(path, ldapi_uri):
     """Create a PHP LDAP admin configuration file.
 
     :param path: Path to write the configuration to.
-    :param setup_path: Function to generate setup paths.
     """
     setup_file(setup_path("phpldapadmin-config.php"), path,
             {"S4_LDAPI_URI": ldapi_uri})
 
 
-def create_zone_file(lp, logger, paths, targetdir, setup_path, dnsdomain,
+def create_dns_dir(logger, paths):
+    """Write out a DNS zone file, from the info in the current database.
+
+    :param logger: Logger object
+    :param paths: paths object
+    """
+    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)
+            # chmod needed to cope with umask
+            os.chmod(dns_dir, 0770)
+        except OSError:
+            if not os.environ.has_key('SAMBA_SELFTEST'):
+                logger.error("Failed to chown %s to bind gid %u" % (
+                    dns_dir, paths.bind_gid))
+
+
+def create_zone_file(lp, logger, paths, targetdir, dnsdomain,
                      hostip, hostip6, hostname, realm, domainguid,
                      ntdsguid):
     """Write out a DNS zone file, from the info in the current database.
 
     :param paths: paths object
-    :param setup_path: Setup path function.
     :param dnsdomain: DNS Domain name
     :param domaindn: DN of the Domain
     :param hostip: Local IPv4 IP
@@ -1822,15 +2073,6 @@ def create_zone_file(lp, logger, paths, targetdir, setup_path, dnsdomain,
         hostip_host_line = ""
         gc_msdcs_ip_line = ""
 
-    dns_dir = os.path.dirname(paths.dns)
-
-    try:
-        shutil.rmtree(dns_dir, True)
-    except OSError:
-        pass
-
-    os.mkdir(dns_dir, 0775)
-
     # we need to freeze the zone while we update the contents
     if targetdir is None:
         rndc = ' '.join(lp.get("rndc command"))
@@ -1852,69 +2094,67 @@ def create_zone_file(lp, logger, paths, targetdir, setup_path, dnsdomain,
             "GC_MSDCS_IP6_LINE": gc_msdcs_ip6_line,
         })
 
-    # note that we use no variable substitution on this file
-    # the substitution is done at runtime by samba_dnsupdate
-    setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
-
-    # and the SPN update list
-    setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
-
     if paths.bind_gid is not None:
         try:
-            os.chown(dns_dir, -1, paths.bind_gid)
             os.chown(paths.dns, -1, paths.bind_gid)
             # chmod needed to cope with umask
-            os.chmod(dns_dir, 0775)
             os.chmod(paths.dns, 0664)
         except OSError:
             if not os.environ.has_key('SAMBA_SELFTEST'):
                 logger.error("Failed to chown %s to bind gid %u" % (
-                    dns_dir, paths.bind_gid))
+                    paths.dns, paths.bind_gid))
 
     if targetdir is None:
         os.system(rndc + " unfreeze " + lp.get("realm"))
 
 
-def create_dns_update_list(lp, logger, paths, setup_path):
+def create_dns_update_list(lp, logger, paths):
     """Write out a dns_update_list file"""
     # note that we use no variable substitution on this file
-    # the substitution is done at runtime by samba_dnsupdate
+    # the substitution is done at runtime by samba_dnsupdate, samba_spnupdate
     setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
     setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
 
 
-def create_named_conf(paths, setup_path, realm, dnsdomain,
-                      private_dir):
+def create_named_conf(paths, realm, dnsdomain, dns_backend):
     """Write out a file containing zone statements suitable for inclusion in a
     named.conf file (including GSS-TSIG configuration).
 
     :param paths: all paths
-    :param setup_path: Setup path function.
     :param realm: Realm name
     :param dnsdomain: DNS Domain name
-    :param private_dir: Path to private directory
+    :param dns_backend: DNS backend type
     :param keytab_name: File name of DNS keytab file
     """
 
-    setup_file(setup_path("named.conf"), paths.namedconf, {
-            "DNSDOMAIN": dnsdomain,
-            "REALM": realm,
-            "ZONE_FILE": paths.dns,
-            "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
-            "NAMED_CONF": paths.namedconf,
-            "NAMED_CONF_UPDATE": paths.namedconf_update
-            })
+    if dns_backend == "BIND9":
+        setup_file(setup_path("named.conf"), paths.namedconf, {
+                    "DNSDOMAIN": dnsdomain,
+                    "REALM": realm,
+                    "ZONE_FILE": paths.dns,
+                    "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
+                    "NAMED_CONF": paths.namedconf,
+                    "NAMED_CONF_UPDATE": paths.namedconf_update
+                    })
+
+        setup_file(setup_path("named.conf.update"), paths.namedconf_update)
+
+    elif dns_backend == "BIND9_DLZ":
+        dlz_module_path = os.path.join(samba.param.modules_dir(), 
+                                        "bind9/dlz_bind9.so")
+        setup_file(setup_path("named.conf.dlz"), paths.namedconf, {
+                    "NAMED_CONF": paths.namedconf,
+                    "BIND9_DLZ_MODULE": dlz_module_path,
+                    })
 
-    setup_file(setup_path("named.conf.update"), paths.namedconf_update)
 
 
-def create_named_txt(path, setup_path, realm, dnsdomain, private_dir,
+def create_named_txt(path, realm, dnsdomain, dnsname, private_dir,
     keytab_name):
     """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 setup_path: Setup path function.
     :param realm: Realm name
     :param dnsdomain: DNS Domain name
     :param private_dir: Path to private directory
@@ -1922,6 +2162,7 @@ def create_named_txt(path, setup_path, realm, dnsdomain, private_dir,
     """
     setup_file(setup_path("named.txt"), path, {
             "DNSDOMAIN": dnsdomain,
+            "DNSNAME" : dnsname, 
             "REALM": realm,
             "DNS_KEYTAB": keytab_name,
             "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
@@ -1929,12 +2170,11 @@ def create_named_txt(path, setup_path, realm, dnsdomain, private_dir,
         })
 
 
-def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
+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).
 
     :param path: Path of the new named.conf file.
-    :param setup_path: Setup path function.
     :param dnsdomain: DNS Domain name
     :param hostname: Local hostname
     :param realm: Realm name