provision: fix string formatting (number of args)
[nivanova/samba-autobuild/.git] / python / samba / provision / __init__.py
index 2387931987e251861cf23cb89896eed06a1bd3d2..1b7762eb12b17113b8f04152c1c2d01fc2a2f452 100644 (file)
 
 __docformat__ = "restructuredText"
 
 
 __docformat__ = "restructuredText"
 
+from samba.compat import urllib_quote
+from samba.compat import string_types
+from samba.compat import binary_type
 from base64 import b64encode
 from base64 import b64encode
+import errno
 import os
 import os
+import stat
 import re
 import pwd
 import grp
 import re
 import pwd
 import grp
@@ -35,8 +40,6 @@ import logging
 import time
 import uuid
 import socket
 import time
 import uuid
 import socket
-import urllib
-import string
 import tempfile
 import samba.dsdb
 
 import tempfile
 import samba.dsdb
 
@@ -44,6 +47,7 @@ import ldb
 
 from samba.auth import system_session, admin_session
 import samba
 
 from samba.auth import system_session, admin_session
 import samba
+from samba import auth
 from samba.samba3 import smbd, passdb
 from samba.samba3 import param as s3param
 from samba.dsdb import DS_DOMAIN_FUNCTION_2000
 from samba.samba3 import smbd, passdb
 from samba.samba3 import param as s3param
 from samba.dsdb import DS_DOMAIN_FUNCTION_2000
@@ -57,27 +61,26 @@ from samba import (
     valid_netbios_name,
     version,
     is_heimdal_built,
     valid_netbios_name,
     version,
     is_heimdal_built,
-    )
+)
 from samba.dcerpc import security, misc
 from samba.dcerpc.misc import (
     SEC_CHAN_BDC,
     SEC_CHAN_WKSTA,
 from samba.dcerpc import security, misc
 from samba.dcerpc.misc import (
     SEC_CHAN_BDC,
     SEC_CHAN_WKSTA,
-    )
+)
 from samba.dsdb import (
     DS_DOMAIN_FUNCTION_2003,
     DS_DOMAIN_FUNCTION_2008_R2,
     ENC_ALL_TYPES,
 from samba.dsdb import (
     DS_DOMAIN_FUNCTION_2003,
     DS_DOMAIN_FUNCTION_2008_R2,
     ENC_ALL_TYPES,
-    )
+)
 from samba.idmap import IDmapDB
 from samba.ms_display_specifiers import read_ms_ldif
 from samba.ntacls import setntacl, getntacl, dsacl2fsacl
 from samba.ndr import ndr_pack, ndr_unpack
 from samba.provision.backend import (
 from samba.idmap import IDmapDB
 from samba.ms_display_specifiers import read_ms_ldif
 from samba.ntacls import setntacl, getntacl, dsacl2fsacl
 from samba.ndr import ndr_pack, ndr_unpack
 from samba.provision.backend import (
-    ExistingBackend,
     FDSBackend,
     LDBBackend,
     OpenLDAPBackend,
     FDSBackend,
     LDBBackend,
     OpenLDAPBackend,
-    )
+)
 from samba.descriptor import (
     get_empty_descriptor,
     get_config_descriptor,
 from samba.descriptor import (
     get_empty_descriptor,
     get_config_descriptor,
@@ -98,7 +101,8 @@ from samba.descriptor import (
     get_dns_partition_descriptor,
     get_dns_forest_microsoft_dns_descriptor,
     get_dns_domain_microsoft_dns_descriptor,
     get_dns_partition_descriptor,
     get_dns_forest_microsoft_dns_descriptor,
     get_dns_domain_microsoft_dns_descriptor,
-    )
+    get_managed_service_accounts_descriptor,
+)
 from samba.provision.common import (
     setup_path,
     setup_add_ldif,
 from samba.provision.common import (
     setup_path,
     setup_add_ldif,
@@ -112,7 +116,7 @@ from samba.provision.sambadns import (
     get_dnsadmins_sid,
     setup_ad_dns,
     create_dns_update_list
     get_dnsadmins_sid,
     setup_ad_dns,
     create_dns_update_list
-    )
+)
 
 import samba.param
 import samba.registry
 
 import samba.param
 import samba.registry
@@ -120,12 +124,15 @@ from samba.schema import Schema
 from samba.samdb import SamDB
 from samba.dbchecker import dbcheck
 from samba.provision.kerberos import create_kdc_conf
 from samba.samdb import SamDB
 from samba.dbchecker import dbcheck
 from samba.provision.kerberos import create_kdc_conf
+from samba.samdb import get_default_backend_store
 
 DEFAULT_POLICY_GUID = "31B2F340-016D-11D2-945F-00C04FB984F9"
 DEFAULT_DC_POLICY_GUID = "6AC1786C-016F-11D2-945F-00C04FB984F9"
 DEFAULTSITE = "Default-First-Site-Name"
 LAST_PROVISION_USN_ATTRIBUTE = "lastProvisionUSN"
 
 
 DEFAULT_POLICY_GUID = "31B2F340-016D-11D2-945F-00C04FB984F9"
 DEFAULT_DC_POLICY_GUID = "6AC1786C-016F-11D2-945F-00C04FB984F9"
 DEFAULTSITE = "Default-First-Site-Name"
 LAST_PROVISION_USN_ATTRIBUTE = "lastProvisionUSN"
 
+DEFAULT_MIN_PWD_LENGTH = 7
+
 
 class ProvisionPaths(object):
 
 
 class ProvisionPaths(object):
 
@@ -145,6 +152,7 @@ class ProvisionPaths(object):
         self.dns = None
         self.winsdb = None
         self.private_dir = None
         self.dns = None
         self.winsdb = None
         self.private_dir = None
+        self.binddns_dir = None
         self.state_dir = None
 
 
         self.state_dir = None
 
 
@@ -173,7 +181,7 @@ class ProvisionNames(object):
 
 
 def find_provision_key_parameters(samdb, secretsdb, idmapdb, paths, smbconf,
 
 
 def find_provision_key_parameters(samdb, secretsdb, idmapdb, paths, smbconf,
-        lp):
+                                  lp):
     """Get key provision parameters (realm, domain, ...) from a given provision
 
     :param samdb: An LDB object connected to the sam.ldb file
     """Get key provision parameters (realm, domain, ...) from a given provision
 
     :param samdb: An LDB object connected to the sam.ldb file
@@ -188,45 +196,45 @@ def find_provision_key_parameters(samdb, secretsdb, idmapdb, paths, smbconf,
     names.adminpass = None
 
     # NT domain, kerberos realm, root dn, domain dn, domain dns name
     names.adminpass = None
 
     # NT domain, kerberos realm, root dn, domain dn, domain dns name
-    names.domain = string.upper(lp.get("workgroup"))
+    names.domain = lp.get("workgroup").upper()
     names.realm = lp.get("realm")
     names.dnsdomain = names.realm.lower()
     basedn = samba.dn_from_dns_name(names.dnsdomain)
     names.realm = lp.get("realm")
     names.dnsdomain = names.realm.lower()
     basedn = samba.dn_from_dns_name(names.dnsdomain)
-    names.realm = string.upper(names.realm)
+    names.realm = names.realm.upper()
     # netbiosname
     # Get the netbiosname first (could be obtained from smb.conf in theory)
     res = secretsdb.search(expression="(flatname=%s)" %
     # 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.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=*)",
 
     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",
-               "namingContexts"])
+                           base="", scope=ldb.SCOPE_BASE,
+                           attrs=["defaultNamingContext", "schemaNamingContext",
+                                  "configurationNamingContext", "rootDomainNamingContext",
+                                  "namingContexts"])
 
 
-    names.configdn = current[0]["configurationNamingContext"][0]
-    names.schemadn = current[0]["schemaNamingContext"][0]
+    names.configdn = str(current[0]["configurationNamingContext"][0])
+    names.schemadn = str(current[0]["schemaNamingContext"][0])
     if not (ldb.Dn(samdb, basedn) == (ldb.Dn(samdb,
     if not (ldb.Dn(samdb, basedn) == (ldb.Dn(samdb,
-                                       current[0]["defaultNamingContext"][0]))):
+                                             current[0]["defaultNamingContext"][0].decode('utf8')))):
         raise ProvisioningError(("basedn in %s (%s) and from %s (%s)"
                                  "is not the same ..." % (paths.samdb,
         raise ProvisioningError(("basedn in %s (%s) and from %s (%s)"
                                  "is not the same ..." % (paths.samdb,
-                                    str(current[0]["defaultNamingContext"][0]),
-                                    paths.smbconf, basedn)))
+                                                          str(current[0]["defaultNamingContext"][0].decode('utf8')),
+                                                          paths.smbconf, basedn)))
 
 
-    names.domaindn=current[0]["defaultNamingContext"][0]
-    names.rootdn=current[0]["rootDomainNamingContext"][0]
-    names.ncs=current[0]["namingContexts"]
+    names.domaindn = str(current[0]["defaultNamingContext"][0])
+    names.rootdn = str(current[0]["rootDomainNamingContext"][0])
+    names.ncs = current[0]["namingContexts"]
     names.dnsforestdn = None
     names.dnsdomaindn = None
 
     for i in range(0, len(names.ncs)):
     names.dnsforestdn = None
     names.dnsdomaindn = None
 
     for i in range(0, len(names.ncs)):
-        nc = names.ncs[i]
+        nc = str(names.ncs[i])
 
         dnsforestdn = "DC=ForestDnsZones,%s" % (str(names.rootdn))
         if nc == dnsforestdn:
 
         dnsforestdn = "DC=ForestDnsZones,%s" % (str(names.rootdn))
         if nc == dnsforestdn:
@@ -240,7 +248,7 @@ def find_provision_key_parameters(samdb, secretsdb, idmapdb, paths, smbconf,
 
     # default site name
     res3 = samdb.search(expression="(objectClass=site)",
 
     # default site name
     res3 = samdb.search(expression="(objectClass=site)",
-        base="CN=Sites," + names.configdn, scope=ldb.SCOPE_ONELEVEL, attrs=["cn"])
+                        base="CN=Sites," + str(names.configdn), scope=ldb.SCOPE_ONELEVEL, attrs=["cn"])
     names.sitename = str(res3[0]["cn"])
 
     # dns hostname and server dn
     names.sitename = str(res3[0]["cn"])
 
     # dns hostname and server dn
@@ -253,26 +261,26 @@ def find_provision_key_parameters(samdb, secretsdb, idmapdb, paths, smbconf,
     names.hostname = str(res4[0]["dNSHostName"]).replace("." + names.dnsdomain, "")
 
     server_res = samdb.search(expression="serverReference=%s" % res4[0].dn,
     names.hostname = str(res4[0]["dNSHostName"]).replace("." + names.dnsdomain, "")
 
     server_res = samdb.search(expression="serverReference=%s" % res4[0].dn,
-                                attrs=[], base=names.configdn)
+                              attrs=[], base=names.configdn)
     names.serverdn = str(server_res[0].dn)
 
     # invocation id/objectguid
     res5 = samdb.search(expression="(objectClass=*)",
     names.serverdn = str(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"])
+                        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,
     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" ])
+                        scope=ldb.SCOPE_BASE, attrs=["objectGUID",
+                                                     "objectSid", "msDS-Behavior-Version"])
     names.domainguid = str(ndr_unpack(misc.GUID, res6[0]["objectGUID"][0]))
     names.domainguid = str(ndr_unpack(misc.GUID, res6[0]["objectGUID"][0]))
-    names.domainsid = ndr_unpack( security.dom_sid, res6[0]["objectSid"][0])
-    names.forestsid = ndr_unpack( security.dom_sid, res6[0]["objectSid"][0])
+    names.domainsid = ndr_unpack(security.dom_sid, res6[0]["objectSid"][0])
+    names.forestsid = ndr_unpack(security.dom_sid, res6[0]["objectSid"][0])
     if res6[0].get("msDS-Behavior-Version") is None or \
     if res6[0].get("msDS-Behavior-Version") is None or \
-        int(res6[0]["msDS-Behavior-Version"][0]) < DS_DOMAIN_FUNCTION_2000:
+            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])
         names.domainlevel = DS_DOMAIN_FUNCTION_2000
     else:
         names.domainlevel = int(res6[0]["msDS-Behavior-Version"][0])
@@ -280,15 +288,15 @@ def find_provision_key_parameters(samdb, secretsdb, idmapdb, paths, smbconf,
     # policy guid
     res7 = samdb.search(expression="(name={%s})" % DEFAULT_POLICY_GUID,
                         base="CN=Policies,CN=System," + basedn,
     # policy guid
     res7 = samdb.search(expression="(name={%s})" % DEFAULT_POLICY_GUID,
                         base="CN=Policies,CN=System," + basedn,
-                        scope=ldb.SCOPE_ONELEVEL, attrs=["cn","displayName"])
-    names.policyid = str(res7[0]["cn"]).replace("{","").replace("}","")
+                        scope=ldb.SCOPE_ONELEVEL, attrs=["cn", "displayName"])
+    names.policyid = str(res7[0]["cn"]).replace("{", "").replace("}", "")
     # dc policy guid
     res8 = samdb.search(expression="(name={%s})" % DEFAULT_DC_POLICY_GUID,
                         base="CN=Policies,CN=System," + basedn,
                         scope=ldb.SCOPE_ONELEVEL,
     # dc policy guid
     res8 = samdb.search(expression="(name={%s})" % DEFAULT_DC_POLICY_GUID,
                         base="CN=Policies,CN=System," + basedn,
                         scope=ldb.SCOPE_ONELEVEL,
-                        attrs=["cn","displayName"])
+                        attrs=["cn", "displayName"])
     if len(res8) == 1:
     if len(res8) == 1:
-        names.policyid_dc = str(res8[0]["cn"]).replace("{","").replace("}","")
+        names.policyid_dc = str(res8[0]["cn"]).replace("{", "").replace("}", "")
     else:
         names.policyid_dc = None
 
     else:
         names.policyid_dc = None
 
@@ -297,8 +305,8 @@ def find_provision_key_parameters(samdb, secretsdb, idmapdb, paths, smbconf,
                           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))
                           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]
+    if str(res9[0]["type"][0]) == "ID_TYPE_BOTH":
+        names.root_gid = int(res9[0]["xidNumber"][0])
     else:
         names.root_gid = pwd.getpwuid(int(res9[0]["xidNumber"][0])).pw_gid
 
     else:
         names.root_gid = pwd.getpwuid(int(res9[0]["xidNumber"][0])).pw_gid
 
@@ -356,15 +364,17 @@ def update_provision_usn(samdb, low, high, id, replace=False):
                              scope=ldb.SCOPE_BASE,
                              attrs=[LAST_PROVISION_USN_ATTRIBUTE, "dn"])
         for e in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
                              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)
+            if not re.search(';', str(e)):
+                e = "%s;%s" % (str(e), id)
             tab.append(str(e))
 
     tab.append("%s-%s;%s" % (low, high, id))
     delta = ldb.Message()
     delta.dn = ldb.Dn(samdb, "@PROVISION")
             tab.append(str(e))
 
     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)
+    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"])
     entry = samdb.search(expression='provisionnerID=*',
                          base="@PROVISION", scope=ldb.SCOPE_BASE,
                          attrs=["provisionnerID"])
@@ -390,12 +400,14 @@ def set_provision_usn(samdb, low, high, id):
 
     delta = ldb.Message()
     delta.dn = ldb.Dn(samdb, "@PROVISION")
 
     delta = ldb.Message()
     delta.dn = ldb.Dn(samdb, "@PROVISION")
-    delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
-        ldb.FLAG_MOD_ADD, LAST_PROVISION_USN_ATTRIBUTE)
+    delta[LAST_PROVISION_USN_ATTRIBUTE] = \
+        ldb.MessageElement(tab,
+                           ldb.FLAG_MOD_ADD,
+                           LAST_PROVISION_USN_ATTRIBUTE)
     samdb.add(delta)
 
 
     samdb.add(delta)
 
 
-def get_max_usn(samdb,basedn):
+def get_max_usn(samdb, basedn):
     """ This function return the biggest USN present in the provision
 
     :param samdb: A LDB object pointing to the sam.ldb
     """ This function return the biggest USN present in the provision
 
     :param samdb: A LDB object pointing to the sam.ldb
@@ -403,11 +415,11 @@ def get_max_usn(samdb,basedn):
                     (ie. DC=foo, DC=bar)
     :return: The biggest USN in the provision"""
 
                     (ie. DC=foo, DC=bar)
     :return: The biggest USN in the provision"""
 
-    res = samdb.search(expression="objectClass=*",base=basedn,
-                         scope=ldb.SCOPE_SUBTREE,attrs=["uSNChanged"],
-                         controls=["search_options:1:2",
-                                   "server_sort:1:1:uSNChanged",
-                                   "paged_results:1:1"])
+    res = samdb.search(expression="objectClass=*", base=basedn,
+                       scope=ldb.SCOPE_SUBTREE, attrs=["uSNChanged"],
+                       controls=["search_options:1:2",
+                                 "server_sort:1:1:uSNChanged",
+                                 "paged_results:1:1"])
     return res[0]["uSNChanged"]
 
 
     return res[0]["uSNChanged"]
 
 
@@ -420,9 +432,10 @@ def get_last_provision_usn(sam):
     """
     try:
         entry = sam.search(expression="%s=*" % 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):
+                           base="@PROVISION", scope=ldb.SCOPE_BASE,
+                           attrs=[LAST_PROVISION_USN_ATTRIBUTE, "provisionnerID"])
+    except ldb.LdbError as e1:
+        (ecode, emsg) = e1.args
         if ecode == ldb.ERR_NO_SUCH_OBJECT:
             return None
         raise
         if ecode == ldb.ERR_NO_SUCH_OBJECT:
             return None
         raise
@@ -499,7 +512,7 @@ def check_install(lp, session_info, credentials):
     if lp.get("realm") == "":
         raise Exception("Realm empty")
     samdb = Ldb(lp.samdb_url(), session_info=session_info,
     if lp.get("realm") == "":
         raise Exception("Realm empty")
     samdb = Ldb(lp.samdb_url(), session_info=session_info,
-            credentials=credentials, lp=lp)
+                credentials=credentials, lp=lp)
     if len(samdb.search("(cn=Administrator)")) != 1:
         raise ProvisioningError("No administrator account found")
 
     if len(samdb.search("(cn=Administrator)")) != 1:
         raise ProvisioningError("No administrator account found")
 
@@ -519,8 +532,12 @@ def findnss(nssfn, names):
     raise KeyError("Unable to find user/group in %r" % names)
 
 
     raise KeyError("Unable to find user/group in %r" % names)
 
 
-findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
-findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
+def findnss_uid(names):
+    return findnss(pwd.getpwnam, names)[2]
+
+
+def findnss_gid(names):
+    return findnss(grp.getgrnam, names)[2]
 
 
 def provision_paths_from_lp(lp, dnsdomain):
 
 
 def provision_paths_from_lp(lp, dnsdomain):
@@ -531,6 +548,7 @@ def provision_paths_from_lp(lp, dnsdomain):
     """
     paths = ProvisionPaths()
     paths.private_dir = lp.get("private dir")
     """
     paths = ProvisionPaths()
     paths.private_dir = lp.get("private dir")
+    paths.binddns_dir = lp.get("binddns dir")
     paths.state_dir = lp.get("state directory")
 
     # This is stored without path prefix for the "privateKeytab" attribute in
     paths.state_dir = lp.get("state directory")
 
     # This is stored without path prefix for the "privateKeytab" attribute in
@@ -543,16 +561,21 @@ def provision_paths_from_lp(lp, dnsdomain):
     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.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")
     paths.spn_update_list = os.path.join(paths.private_dir, "spn_update_list")
     paths.dns_update_list = os.path.join(paths.private_dir, "dns_update_list")
     paths.spn_update_list = os.path.join(paths.private_dir, "spn_update_list")
-    paths.namedconf = os.path.join(paths.private_dir, "named.conf")
-    paths.namedconf_update = os.path.join(paths.private_dir, "named.conf.update")
-    paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
     paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
     paths.kdcconf = os.path.join(paths.private_dir, "kdc.conf")
     paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
     paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
     paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
     paths.kdcconf = os.path.join(paths.private_dir, "kdc.conf")
     paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
     paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
+    paths.encrypted_secrets_key_path = os.path.join(
+        paths.private_dir,
+        "encrypted_secrets.key")
+
+    paths.dns = os.path.join(paths.binddns_dir, "dns", dnsdomain + ".zone")
+    paths.namedconf = os.path.join(paths.binddns_dir, "named.conf")
+    paths.namedconf_update = os.path.join(paths.binddns_dir, "named.conf.update")
+    paths.namedtxt = os.path.join(paths.binddns_dir, "named.txt")
+
     paths.hklm = "hklm.ldb"
     paths.hkcr = "hkcr.ldb"
     paths.hkcu = "hkcu.ldb"
     paths.hklm = "hklm.ldb"
     paths.hkcr = "hkcr.ldb"
     paths.hkcu = "hkcu.ldb"
@@ -591,7 +614,9 @@ def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
     if dnsdomain is None:
         dnsdomain = lp.get("realm")
         if dnsdomain is None or dnsdomain == "":
     if dnsdomain is None:
         dnsdomain = lp.get("realm")
         if dnsdomain is None or dnsdomain == "":
-            raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp.configfile)
+            raise ProvisioningError(
+                "guess_names: 'realm' not specified in supplied %s!" %
+                lp.configfile)
 
     dnsdomain = dnsdomain.lower()
 
 
     dnsdomain = dnsdomain.lower()
 
@@ -642,8 +667,24 @@ def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
     if domain == realm and not domain_names_forced:
         raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
 
     if domain == realm and not domain_names_forced:
         raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
 
+    if serverrole != "active directory domain controller":
+        #
+        # This is the code path for a domain member
+        # where we provision the database as if we where
+        # on a domain controller, so we should not use
+        # the same dnsdomain as the domain controllers
+        # of our primary domain.
+        #
+        # This will be important if we start doing
+        # SID/name filtering and reject the local
+        # sid and names if they come from a domain
+        # controller.
+        #
+        realm = netbiosname
+        dnsdomain = netbiosname.lower()
+
     if rootdn is None:
     if rootdn is None:
-       rootdn = domaindn
+        rootdn = domaindn
 
     if configdn is None:
         configdn = "CN=Configuration," + rootdn
 
     if configdn is None:
         configdn = "CN=Configuration," + rootdn
@@ -670,6 +711,7 @@ def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
 
     return names
 
 
     return names
 
+
 def make_smbconf(smbconf, hostname, domain, realm, targetdir,
                  serverrole=None, eadb=False, use_ntvfs=False, lp=None,
                  global_param=None):
 def make_smbconf(smbconf, hostname, domain, realm, targetdir,
                  serverrole=None, eadb=False, use_ntvfs=False, lp=None,
                  global_param=None):
@@ -696,11 +738,11 @@ def make_smbconf(smbconf, hostname, domain, realm, targetdir,
         "workgroup": domain,
         "realm": realm,
         "server role": serverrole,
         "workgroup": domain,
         "realm": realm,
         "server role": serverrole,
-        }
+    }
 
     if lp is None:
         lp = samba.param.LoadParm()
 
     if lp is None:
         lp = samba.param.LoadParm()
-    #Load non-existent file
+    # Load non-existent file
     if os.path.exists(smbconf):
         lp.load(smbconf)
 
     if os.path.exists(smbconf):
         lp.load(smbconf)
 
@@ -714,10 +756,12 @@ def make_smbconf(smbconf, hostname, domain, realm, targetdir,
         global_settings["lock dir"] = 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"))
         global_settings["lock dir"] = 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"))
+        global_settings["binddns dir"] = os.path.abspath(os.path.join(targetdir, "bind-dns"))
 
         lp.set("lock dir", os.path.abspath(targetdir))
 
         lp.set("lock dir", os.path.abspath(targetdir))
-        lp.set("state directory",  global_settings["state directory"])
+        lp.set("state directory", global_settings["state directory"])
         lp.set("cache directory", global_settings["cache directory"])
         lp.set("cache directory", global_settings["cache directory"])
+        lp.set("binddns dir", global_settings["binddns dir"])
 
     if eadb:
         if use_ntvfs and not lp.get("posix:eadb"):
 
     if eadb:
         if use_ntvfs and not lp.get("posix:eadb"):
@@ -737,18 +781,18 @@ def make_smbconf(smbconf, hostname, domain, realm, targetdir,
     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(),
     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")
+                                          "scripts")
     else:
         global_settings["passdb backend"] = "samba_dsdb"
 
     f = open(smbconf, 'w')
     try:
         f.write("[globals]\n")
     else:
         global_settings["passdb backend"] = "samba_dsdb"
 
     f = open(smbconf, 'w')
     try:
         f.write("[globals]\n")
-        for key, val in global_settings.iteritems():
+        for key, val in global_settings.items():
             f.write("\t%s = %s\n" % (key, val))
         f.write("\n")
 
             f.write("\t%s = %s\n" % (key, val))
         f.write("\n")
 
-        for name, path in shares.iteritems():
+        for name, path in shares.items():
             f.write("[%s]\n" % name)
             f.write("\tpath = %s\n" % path)
             f.write("\tread only = no\n")
             f.write("[%s]\n" % name)
             f.write("\tpath = %s\n" % path)
             f.write("\tread only = no\n")
@@ -785,7 +829,8 @@ def setup_name_mappings(idmap, sid, root_uid, nobody_uid,
 
 def setup_samdb_partitions(samdb_path, logger, lp, session_info,
                            provision_backend, names, serverrole,
 
 def setup_samdb_partitions(samdb_path, logger, lp, session_info,
                            provision_backend, names, serverrole,
-                           erase=False):
+                           erase=False, plaintext_secrets=False,
+                           backend_store=None):
     """Setup the partitions for the SAM database.
 
     Alternatively, provision() may call this, and then populate the database.
     """Setup the partitions for the SAM database.
 
     Alternatively, provision() may call this, and then populate the database.
@@ -814,17 +859,36 @@ def setup_samdb_partitions(samdb_path, logger, lp, session_info,
     if provision_backend.type != "ldb":
         ldap_backend_line = "ldapBackend: %s" % provision_backend.ldap_uri
 
     if provision_backend.type != "ldb":
         ldap_backend_line = "ldapBackend: %s" % provision_backend.ldap_uri
 
+    required_features = None
+    if not plaintext_secrets:
+        required_features = "requiredFeatures: encryptedSecrets"
+
+    if backend_store is None:
+        backend_store = get_default_backend_store()
+    backend_store_line = "backendStore: %s" % backend_store
+
+    if backend_store == "mdb":
+        if required_features is not None:
+            required_features += "\n"
+        else:
+            required_features = ""
+        required_features += "requiredFeatures: lmdbLevelOne"
+
+    if required_features is None:
+        required_features = "# No required features"
+
     samdb.transaction_start()
     try:
         logger.info("Setting up sam.ldb partitions and settings")
         setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
     samdb.transaction_start()
     try:
         logger.info("Setting up sam.ldb partitions and settings")
         setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
-                "LDAP_BACKEND_LINE": ldap_backend_line
+                "LDAP_BACKEND_LINE": ldap_backend_line,
+                "BACKEND_STORE": backend_store_line
         })
 
         })
 
-
         setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
                 "BACKEND_TYPE": provision_backend.type,
         setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
                 "BACKEND_TYPE": provision_backend.type,
-                "SERVER_ROLE": serverrole
+                "SERVER_ROLE": serverrole,
+                "REQUIRED_FEATURES": required_features
                 })
 
         logger.info("Setting up sam.ldb rootDSE")
                 })
 
         logger.info("Setting up sam.ldb rootDSE")
@@ -848,11 +912,11 @@ def secretsdb_self_join(secretsdb, domain,
     :param machinepass: Machine password
     """
     attrs = ["whenChanged",
     :param machinepass: Machine password
     """
     attrs = ["whenChanged",
-           "secret",
-           "priorSecret",
-           "priorChanged",
-           "krb5Keytab",
-           "privateKeytab"]
+             "secret",
+             "priorSecret",
+             "priorChanged",
+             "krb5Keytab",
+             "privateKeytab"]
 
     if realm is not None:
         if dnsdomain is None:
 
     if realm is not None:
         if dnsdomain is None:
@@ -885,8 +949,8 @@ 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,
     # 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)(!(distinguishedName=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
-        scope=ldb.SCOPE_ONELEVEL)
+                           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:
         secretsdb.delete(del_msg.dn)
 
     for del_msg in res:
         secretsdb.delete(del_msg.dn)
@@ -916,11 +980,11 @@ def secretsdb_self_join(secretsdb, domain,
         secretsdb.modify(msg)
         secretsdb.rename(res[0].dn, msg.dn)
     else:
         secretsdb.modify(msg)
         secretsdb.rename(res[0].dn, msg.dn)
     else:
-        spn = [ 'HOST/%s' % shortname ]
+        spn = ['HOST/%s' % shortname]
         if secure_channel_type == SEC_CHAN_BDC and dnsname is not None:
             # we are a domain controller then we add servicePrincipalName
             # entries for the keytab code to update.
         if secure_channel_type == SEC_CHAN_BDC and dnsname is not None:
             # we are a domain controller then we add servicePrincipalName
             # entries for the keytab code to update.
-            spn.extend([ 'HOST/%s' % dnsname ])
+            spn.extend(['HOST/%s' % dnsname])
         msg["servicePrincipalName"] = spn
 
         secretsdb.add(msg)
         msg["servicePrincipalName"] = spn
 
         secretsdb.add(msg)
@@ -945,6 +1009,10 @@ def setup_secretsdb(paths, session_info, backend_credentials, lp):
     if os.path.exists(keytab_path):
         os.unlink(keytab_path)
 
     if os.path.exists(keytab_path):
         os.unlink(keytab_path)
 
+    bind_dns_keytab_path = os.path.join(paths.binddns_dir, paths.dns_keytab)
+    if os.path.exists(bind_dns_keytab_path):
+        os.unlink(bind_dns_keytab_path)
+
     dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
     if os.path.exists(dns_keytab_path):
         os.unlink(dns_keytab_path)
     dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
     if os.path.exists(dns_keytab_path):
         os.unlink(dns_keytab_path)
@@ -963,17 +1031,17 @@ def setup_secretsdb(paths, session_info, backend_credentials, lp):
             backend_credentials.authentication_requested()):
             if backend_credentials.get_bind_dn() is not None:
                 setup_add_ldif(secrets_ldb,
             backend_credentials.authentication_requested()):
             if backend_credentials.get_bind_dn() is not None:
                 setup_add_ldif(secrets_ldb,
-                    setup_path("secrets_simple_ldap.ldif"), {
-                        "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
-                        "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
-                        })
+                               setup_path("secrets_simple_ldap.ldif"), {
+                                   "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
+                                   "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password()).decode('utf8')
+                               })
             else:
                 setup_add_ldif(secrets_ldb,
             else:
                 setup_add_ldif(secrets_ldb,
-                    setup_path("secrets_sasl_ldap.ldif"), {
-                        "LDAPADMINUSER": backend_credentials.get_username(),
-                        "LDAPADMINREALM": backend_credentials.get_realm(),
-                        "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
-                        })
+                               setup_path("secrets_sasl_ldap.ldif"), {
+                                   "LDAPADMINUSER": backend_credentials.get_username(),
+                                   "LDAPADMINREALM": backend_credentials.get_realm(),
+                                   "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password()).decode('utf8')
+                               })
     except:
         secrets_ldb.transaction_cancel()
         raise
     except:
         secrets_ldb.transaction_cancel()
         raise
@@ -996,6 +1064,31 @@ def setup_privileges(path, session_info, lp):
     privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
 
 
     privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
 
 
+def setup_encrypted_secrets_key(path):
+    """Setup the encrypted secrets key file.
+
+    Any existing key file will be deleted and a new random key generated.
+
+    :param path: Path to the secrets key file.
+
+    """
+    if os.path.exists(path):
+        os.unlink(path)
+
+    flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL
+    mode = stat.S_IRUSR | stat.S_IWUSR
+
+    umask_original = os.umask(0)
+    try:
+        fd = os.open(path, flags, mode)
+    finally:
+        os.umask(umask_original)
+
+    with os.fdopen(fd, 'wb') as f:
+        key = samba.generate_random_bytes(16)
+        f.write(key)
+
+
 def setup_registry(path, session_info, lp):
     """Setup the registry.
 
 def setup_registry(path, session_info, lp):
     """Setup the registry.
 
@@ -1037,20 +1130,20 @@ def setup_samdb_rootdse(samdb, names):
     setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
         "SCHEMADN": names.schemadn,
         "DOMAINDN": names.domaindn,
     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,
         "CONFIGDN": names.configdn,
         "SERVERDN": names.serverdn,
-        })
+    })
 
 
 def setup_self_join(samdb, admin_session_info, names, fill, machinepass,
 
 
 def setup_self_join(samdb, admin_session_info, names, fill, machinepass,
-        dns_backend, dnspass, domainsid, next_rid, invocationid,
-        policyguid, policyguid_dc,
-        domainControllerFunctionality, ntdsguid=None, dc_rid=None):
+                    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)
     if ntdsguid is not None:
     """Join a host to its own domain."""
     assert isinstance(invocationid, str)
     if ntdsguid is not None:
-        ntdsguid_line = "objectGUID: %s\n"%ntdsguid
+        ntdsguid_line = "objectGUID: %s\n" % ntdsguid
     else:
         ntdsguid_line = ""
 
     else:
         ntdsguid_line = ""
 
@@ -1065,7 +1158,7 @@ def setup_self_join(samdb, admin_session_info, names, fill, machinepass,
               "INVOCATIONID": invocationid,
               "NETBIOSNAME": names.netbiosname,
               "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
               "INVOCATIONID": invocationid,
               "NETBIOSNAME": names.netbiosname,
               "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
-              "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
+              "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')).decode('utf8'),
               "DOMAINSID": str(domainsid),
               "DCRID": str(dc_rid),
               "SAMBA_VERSION_STRING": version,
               "DOMAINSID": str(domainsid),
               "DCRID": str(dc_rid),
               "SAMBA_VERSION_STRING": version,
@@ -1092,7 +1185,7 @@ def setup_self_join(samdb, admin_session_info, names, fill, machinepass,
                 "INVOCATIONID": invocationid,
                 "NETBIOSNAME": names.netbiosname,
                 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
                 "INVOCATIONID": invocationid,
                 "NETBIOSNAME": names.netbiosname,
                 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
-                "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
+                "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')).decode('utf8'),
                 "DOMAINSID": str(domainsid),
                 "DCRID": str(dc_rid),
                 "SAMBA_VERSION_STRING": version,
                 "DOMAINSID": str(domainsid),
                 "DCRID": str(dc_rid),
                 "SAMBA_VERSION_STRING": version,
@@ -1102,13 +1195,13 @@ def setup_self_join(samdb, admin_session_info, names, fill, machinepass,
 
     # Setup fSMORoleOwner entries to point at the newly created DC entry
         setup_modify_ldif(samdb,
 
     # 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,
-                })
+                          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)
 
     system_session_info = system_session()
     samdb.set_session_info(system_session_info)
@@ -1129,9 +1222,9 @@ def setup_self_join(samdb, admin_session_info, names, fill, machinepass,
         setup_add_ldif(samdb, setup_path("provision_dns_add_samba.ldif"), {
               "DNSDOMAIN": names.dnsdomain,
               "DOMAINDN": names.domaindn,
         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')),
-              "HOSTNAME" : names.hostname,
-              "DNSNAME" : '%s.%s' % (
+              "DNSPASS_B64": b64encode(dnspass.encode('utf-16-le')).decode('utf8'),
+              "HOSTNAME": names.hostname,
+              "DNSNAME": '%s.%s' % (
                   names.netbiosname.lower(), names.dnsdomain.lower())
               })
 
                   names.netbiosname.lower(), names.dnsdomain.lower())
               })
 
@@ -1152,7 +1245,7 @@ def getpolicypath(sysvolpath, dnsdomain, guid):
 
 def create_gpo_struct(policy_path):
     if not os.path.exists(policy_path):
 
 def create_gpo_struct(policy_path):
     if not os.path.exists(policy_path):
-        os.makedirs(policy_path, 0775)
+        os.makedirs(policy_path, 0o775)
     f = open(os.path.join(policy_path, "GPT.INI"), 'w')
     try:
         f.write("[General]\r\nVersion=0")
     f = open(os.path.join(policy_path, "GPT.INI"), 'w')
     try:
         f.write("[General]\r\nVersion=0")
@@ -1160,10 +1253,10 @@ def create_gpo_struct(policy_path):
         f.close()
     p = os.path.join(policy_path, "MACHINE")
     if not os.path.exists(p):
         f.close()
     p = os.path.join(policy_path, "MACHINE")
     if not os.path.exists(p):
-        os.makedirs(p, 0775)
+        os.makedirs(p, 0o775)
     p = os.path.join(policy_path, "USER")
     if not os.path.exists(p):
     p = os.path.join(policy_path, "USER")
     if not os.path.exists(p):
-        os.makedirs(p, 0775)
+        os.makedirs(p, 0o775)
 
 
 def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
 
 
 def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
@@ -1174,15 +1267,16 @@ def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
     :param policyguid: GUID of the default domain policy
     :param policyguid_dc: GUID of the default domain controler policy
     """
     :param policyguid: GUID of the default domain policy
     :param policyguid_dc: GUID of the default domain controler policy
     """
-    policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
+    policy_path = getpolicypath(sysvolpath, dnsdomain, policyguid)
     create_gpo_struct(policy_path)
 
     create_gpo_struct(policy_path)
 
-    policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
+    policy_path = getpolicypath(sysvolpath, dnsdomain, policyguid_dc)
     create_gpo_struct(policy_path)
 
 
 def setup_samdb(path, session_info, provision_backend, lp, names,
     create_gpo_struct(policy_path)
 
 
 def setup_samdb(path, session_info, provision_backend, lp, names,
-        logger, fill, serverrole, schema, am_rodc=False):
+                logger, fill, serverrole, schema, am_rodc=False,
+                plaintext_secrets=False, backend_store=None):
     """Setup a complete SAM Database.
 
     :note: This will wipe the main SAM database file!
     """Setup a complete SAM Database.
 
     :note: This will wipe the main SAM database file!
@@ -1190,8 +1284,9 @@ def setup_samdb(path, session_info, provision_backend, lp, names,
 
     # Also wipes the database
     setup_samdb_partitions(path, logger=logger, lp=lp,
 
     # Also wipes the database
     setup_samdb_partitions(path, logger=logger, lp=lp,
-        provision_backend=provision_backend, session_info=session_info,
-        names=names, serverrole=serverrole)
+                           provision_backend=provision_backend, session_info=session_info,
+                           names=names, serverrole=serverrole, plaintext_secrets=plaintext_secrets,
+                           backend_store=backend_store)
 
     # Load the database, but don's load the global schema and don't connect
     # quite yet
 
     # Load the database, but don's load the global schema and don't connect
     # quite yet
@@ -1212,7 +1307,8 @@ def setup_samdb(path, session_info, provision_backend, lp, names,
     # DB
     try:
         samdb.connect(path)
     # DB
     try:
         samdb.connect(path)
-    except ldb.LdbError, (num, string_error):
+    except ldb.LdbError as e2:
+        (num, string_error) = e2.args
         if (num == ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS):
             raise ProvisioningError("Permission denied connecting to %s, are you running as root?" % path)
         else:
         if (num == ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS):
             raise ProvisioningError("Permission denied connecting to %s, are you running as root?" % path)
         else:
@@ -1227,9 +1323,10 @@ def setup_samdb(path, session_info, provision_backend, lp, names,
 
 
 def fill_samdb(samdb, lp, names, logger, policyguid,
 
 
 def fill_samdb(samdb, lp, names, logger, 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):
+               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,
+               backend_store=None):
 
     if next_rid is None:
         next_rid = 1000
 
     if next_rid is None:
         next_rid = 1000
@@ -1269,7 +1366,7 @@ def fill_samdb(samdb, lp, names, logger, policyguid,
     samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
     samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
     samdb.set_opaque_integer("domainControllerFunctionality",
     samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
     samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
     samdb.set_opaque_integer("domainControllerFunctionality",
-        domainControllerFunctionality)
+                             domainControllerFunctionality)
 
     samdb.set_domain_sid(str(names.domainsid))
     samdb.set_invocation_id(invocationid)
 
     samdb.set_domain_sid(str(names.domainsid))
     samdb.set_invocation_id(invocationid)
@@ -1284,7 +1381,7 @@ def fill_samdb(samdb, lp, names, logger, policyguid,
     else:
         domainguid_line = ""
 
     else:
         domainguid_line = ""
 
-    descr = b64encode(get_domain_descriptor(names.domainsid))
+    descr = b64encode(get_domain_descriptor(names.domainsid)).decode('utf8')
     setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
             "DOMAINDN": names.domaindn,
             "DOMAINSID": str(names.domainsid),
     setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
             "DOMAINDN": names.domaindn,
             "DOMAINSID": str(names.domainsid),
@@ -1300,13 +1397,14 @@ def fill_samdb(samdb, lp, names, logger, policyguid,
         "CONFIGDN": names.configdn,
         "POLICYGUID": policyguid,
         "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
         "CONFIGDN": names.configdn,
         "POLICYGUID": policyguid,
         "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
-        "SAMBA_VERSION_STRING": version
-        })
+        "SAMBA_VERSION_STRING": version,
+        "MIN_PWD_LENGTH": str(DEFAULT_MIN_PWD_LENGTH)
+    })
 
     # 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")
 
     # 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(names.domainsid))
+        descr = b64encode(get_config_descriptor(names.domainsid)).decode('utf8')
         setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
                 "CONFIGDN": names.configdn,
                 "DESCRIPTOR": descr,
         setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
                 "CONFIGDN": names.configdn,
                 "DESCRIPTOR": descr,
@@ -1327,8 +1425,8 @@ def fill_samdb(samdb, lp, names, logger, policyguid,
 
     # Now register this container in the root of the forest
     msg = ldb.Message(ldb.Dn(samdb, names.domaindn))
 
     # 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")
+    msg["subRefs"] = ldb.MessageElement(names.configdn, ldb.FLAG_MOD_ADD,
+                                        "subRefs")
 
     samdb.invocation_id = invocationid
 
 
     samdb.invocation_id = invocationid
 
@@ -1336,12 +1434,18 @@ def fill_samdb(samdb, lp, names, logger, policyguid,
     if fill == FILL_FULL:
         logger.info("Setting up sam.ldb configuration data")
 
     if fill == FILL_FULL:
         logger.info("Setting up sam.ldb configuration data")
 
-        partitions_descr = b64encode(get_config_partitions_descriptor(names.domainsid))
-        sites_descr = b64encode(get_config_sites_descriptor(names.domainsid))
-        ntdsquotas_descr = b64encode(get_config_ntds_quotas_descriptor(names.domainsid))
-        protected1_descr = b64encode(get_config_delete_protected1_descriptor(names.domainsid))
-        protected1wd_descr = b64encode(get_config_delete_protected1wd_descriptor(names.domainsid))
-        protected2_descr = b64encode(get_config_delete_protected2_descriptor(names.domainsid))
+        partitions_descr = b64encode(get_config_partitions_descriptor(names.domainsid)).decode('utf8')
+        sites_descr = b64encode(get_config_sites_descriptor(names.domainsid)).decode('utf8')
+        ntdsquotas_descr = b64encode(get_config_ntds_quotas_descriptor(names.domainsid)).decode('utf8')
+        protected1_descr = b64encode(get_config_delete_protected1_descriptor(names.domainsid)).decode('utf8')
+        protected1wd_descr = b64encode(get_config_delete_protected1wd_descriptor(names.domainsid)).decode('utf8')
+        protected2_descr = b64encode(get_config_delete_protected2_descriptor(names.domainsid)).decode('utf8')
+
+        if "2008" in schema.base_schema:
+            # exclude 2012-specific changes if we're using a 2008 schema
+            incl_2012 = "#"
+        else:
+            incl_2012 = ""
 
         setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
                 "CONFIGDN": names.configdn,
 
         setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
                 "CONFIGDN": names.configdn,
@@ -1364,6 +1468,11 @@ def fill_samdb(samdb, lp, names, logger, policyguid,
                 "SITES_DESCRIPTOR": sites_descr,
                 })
 
                 "SITES_DESCRIPTOR": sites_descr,
                 })
 
+        setup_add_ldif(samdb, setup_path("extended-rights.ldif"), {
+                "CONFIGDN": names.configdn,
+                "INC2012": incl_2012,
+                })
+
         logger.info("Setting up display specifiers")
         display_specifiers_ldif = read_ms_ldif(
             setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
         logger.info("Setting up display specifiers")
         display_specifiers_ldif = read_ms_ldif(
             setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
@@ -1372,15 +1481,15 @@ def fill_samdb(samdb, lp, names, logger, policyguid,
         check_all_substituted(display_specifiers_ldif)
         samdb.add_ldif(display_specifiers_ldif)
 
         check_all_substituted(display_specifiers_ldif)
         samdb.add_ldif(display_specifiers_ldif)
 
-        logger.info("Modifying display specifiers")
+        logger.info("Modifying display specifiers and extended rights")
         setup_modify_ldif(samdb,
         setup_modify_ldif(samdb,
-            setup_path("provision_configuration_modify.ldif"), {
-            "CONFIGDN": names.configdn,
-            "DISPLAYSPECIFIERS_DESCRIPTOR": protected2_descr
-            })
+                          setup_path("provision_configuration_modify.ldif"), {
+                              "CONFIGDN": names.configdn,
+                              "DISPLAYSPECIFIERS_DESCRIPTOR": protected2_descr
+                          })
 
     logger.info("Adding users container")
 
     logger.info("Adding users container")
-    users_desc = b64encode(get_domain_users_descriptor(names.domainsid))
+    users_desc = b64encode(get_domain_users_descriptor(names.domainsid)).decode('utf8')
     setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
             "DOMAINDN": names.domaindn,
             "USERS_DESCRIPTOR": users_desc
     setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
             "DOMAINDN": names.domaindn,
             "USERS_DESCRIPTOR": users_desc
@@ -1389,21 +1498,21 @@ def fill_samdb(samdb, lp, names, logger, policyguid,
     setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
             "DOMAINDN": names.domaindn})
     logger.info("Adding computers 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(names.domainsid))
+    computers_desc = b64encode(get_domain_computers_descriptor(names.domainsid)).decode('utf8')
     setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
             "DOMAINDN": names.domaindn,
             "COMPUTERS_DESCRIPTOR": computers_desc
             })
     logger.info("Modifying computers container")
     setup_modify_ldif(samdb,
     setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
             "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})
+                      setup_path("provision_computers_modify.ldif"), {
+                          "DOMAINDN": names.domaindn})
     logger.info("Setting up sam.ldb data")
     logger.info("Setting up sam.ldb data")
-    infrastructure_desc = b64encode(get_domain_infrastructure_descriptor(names.domainsid))
-    lostandfound_desc = b64encode(get_domain_delete_protected2_descriptor(names.domainsid))
-    system_desc = b64encode(get_domain_delete_protected1_descriptor(names.domainsid))
-    builtin_desc = b64encode(get_domain_builtin_descriptor(names.domainsid))
-    controllers_desc = b64encode(get_domain_controllers_descriptor(names.domainsid))
+    infrastructure_desc = b64encode(get_domain_infrastructure_descriptor(names.domainsid)).decode('utf8')
+    lostandfound_desc = b64encode(get_domain_delete_protected2_descriptor(names.domainsid)).decode('utf8')
+    system_desc = b64encode(get_domain_delete_protected1_descriptor(names.domainsid)).decode('utf8')
+    builtin_desc = b64encode(get_domain_builtin_descriptor(names.domainsid)).decode('utf8')
+    controllers_desc = b64encode(get_domain_controllers_descriptor(names.domainsid)).decode('utf8')
     setup_add_ldif(samdb, setup_path("provision.ldif"), {
         "CREATTIME": str(samba.unix2nttime(int(time.time()))),
         "DOMAINDN": names.domaindn,
     setup_add_ldif(samdb, setup_path("provision.ldif"), {
         "CREATTIME": str(samba.unix2nttime(int(time.time()))),
         "DOMAINDN": names.domaindn,
@@ -1418,70 +1527,74 @@ def fill_samdb(samdb, lp, names, logger, policyguid,
         "SYSTEM_DESCRIPTOR": system_desc,
         "BUILTIN_DESCRIPTOR": builtin_desc,
         "DOMAIN_CONTROLLERS_DESCRIPTOR": controllers_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
     if fill == FILL_FULL:
 
     # 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:
+        managedservice_descr = b64encode(get_managed_service_accounts_descriptor(names.domainsid)).decode('utf8')
         setup_modify_ldif(samdb,
                           setup_path("provision_configuration_references.ldif"), {
         setup_modify_ldif(samdb,
                           setup_path("provision_configuration_references.ldif"), {
-                "CONFIGDN": names.configdn,
-                "SCHEMADN": names.schemadn})
+                              "CONFIGDN": names.configdn,
+                              "SCHEMADN": names.schemadn})
 
         logger.info("Setting up well known security principals")
 
         logger.info("Setting up well known security principals")
-        protected1wd_descr = b64encode(get_config_delete_protected1wd_descriptor(names.domainsid))
+        protected1wd_descr = b64encode(get_config_delete_protected1wd_descriptor(names.domainsid)).decode('utf8')
         setup_add_ldif(samdb, setup_path("provision_well_known_sec_princ.ldif"), {
             "CONFIGDN": names.configdn,
             "WELLKNOWNPRINCIPALS_DESCRIPTOR": protected1wd_descr,
         setup_add_ldif(samdb, setup_path("provision_well_known_sec_princ.ldif"), {
             "CONFIGDN": names.configdn,
             "WELLKNOWNPRINCIPALS_DESCRIPTOR": protected1wd_descr,
-            })
+        }, controls=["relax:0", "provision:0"])
 
     if fill == FILL_FULL or fill == FILL_SUBDOMAIN:
         setup_modify_ldif(samdb,
 
     if fill == FILL_FULL or fill == FILL_SUBDOMAIN:
         setup_modify_ldif(samdb,
-                          setup_path("provision_basedn_references.ldif"),
-                          {"DOMAINDN": names.domaindn})
+                          setup_path("provision_basedn_references.ldif"), {
+                              "DOMAINDN": names.domaindn,
+                              "MANAGEDSERVICE_DESCRIPTOR": managedservice_descr
+                          })
 
         logger.info("Setting up sam.ldb users and groups")
         setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
             "DOMAINDN": names.domaindn,
             "DOMAINSID": str(names.domainsid),
 
         logger.info("Setting up sam.ldb users and groups")
         setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
             "DOMAINDN": names.domaindn,
             "DOMAINSID": str(names.domainsid),
-            "ADMINPASS_B64": b64encode(adminpass.encode('utf-16-le')),
-            "KRBTGTPASS_B64": b64encode(krbtgtpass.encode('utf-16-le'))
-            })
+            "ADMINPASS_B64": b64encode(adminpass.encode('utf-16-le')).decode('utf8'),
+            "KRBTGTPASS_B64": b64encode(krbtgtpass.encode('utf-16-le')).decode('utf8')
+        }, controls=["relax:0", "provision:0"])
 
         logger.info("Setting up self join")
         setup_self_join(samdb, admin_session_info, names=names, fill=fill,
 
         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=names.domainsid,
-            next_rid=next_rid,
-            dc_rid=dc_rid,
-            policyguid=policyguid,
-            policyguid_dc=policyguid_dc,
-            domainControllerFunctionality=domainControllerFunctionality,
-            ntdsguid=ntdsguid)
+                        invocationid=invocationid,
+                        dns_backend=dns_backend,
+                        dnspass=dnspass,
+                        machinepass=machinepass,
+                        domainsid=names.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,
 
         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)
+                                         attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE).decode('utf8')
+        assert isinstance(names.ntdsguid, string_types)
 
     return samdb
 
 
 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)"
 
     return samdb
 
 
 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"
+SYSVOL_SERVICE = "sysvol"
+
 
 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,
 
 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,
-                    use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
+                     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,
         for name in dirs:
             setntacl(lp, os.path.join(root, name), acl, domsid,
-                    use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
+                     use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
 
 
 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb):
 
 
 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb):
@@ -1499,15 +1612,15 @@ def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, p
     # Set ACL for GPO root folder
     root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
     setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid),
     # Set ACL for GPO root folder
     root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
     setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid),
-            use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=SYSVOL_SERVICE)
+             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"],
-                        expression="", scope=ldb.SCOPE_ONELEVEL)
+    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,
 
     for policy in res:
         acl = ndr_unpack(security.descriptor,
-                         str(policy["nTSecurityDescriptor"])).as_sddl()
+                         policy["nTSecurityDescriptor"][0]).as_sddl()
         policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
         set_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
                     str(domainsid), use_ntvfs,
         policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
         set_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
                     str(domainsid), use_ntvfs,
@@ -1515,7 +1628,7 @@ def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, p
 
 
 def setsysvolacl(samdb, netlogon, sysvol, uid, gid, domainsid, dnsdomain,
 
 
 def setsysvolacl(samdb, netlogon, sysvol, uid, gid, domainsid, dnsdomain,
-        domaindn, lp, use_ntvfs):
+                 domaindn, lp, use_ntvfs):
     """Set the ACL for the sysvol share and the subfolders
 
     :param samdb: An LDB object on the SAM db
     """Set the ACL for the sysvol share and the subfolders
 
     :param samdb: An LDB object on the SAM db
@@ -1536,7 +1649,7 @@ def setsysvolacl(samdb, netlogon, sysvol, uid, gid, domainsid, dnsdomain,
         file = tempfile.NamedTemporaryFile(dir=os.path.abspath(sysvol))
         try:
             try:
         file = tempfile.NamedTemporaryFile(dir=os.path.abspath(sysvol))
         try:
             try:
-                smbd.set_simple_acl(file.name, 0755, gid)
+                smbd.set_simple_acl(file.name, 0o755, gid)
             except OSError:
                 if not smbd.have_posix_acls():
                     # This clue is only strictly correct for RPM and
             except OSError:
                 if not smbd.have_posix_acls():
                     # This clue is only strictly correct for RPM and
@@ -1578,7 +1691,6 @@ def setsysvolacl(samdb, netlogon, sysvol, uid, gid, domainsid, dnsdomain,
         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()))
 
         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:
         if use_ntvfs:
             os.chown(sysvol, -1, gid)
     try:
         if use_ntvfs:
             os.chown(sysvol, -1, gid)
@@ -1587,33 +1699,47 @@ def setsysvolacl(samdb, netlogon, sysvol, uid, gid, domainsid, dnsdomain,
     else:
         canchown = True
 
     else:
         canchown = True
 
+    # use admin sid dn as user dn, since admin should own most of the files,
+    # the operation will be much faster
+    userdn = '<SID={}-{}>'.format(domainsid, security.DOMAIN_RID_ADMINISTRATOR)
+
+    flags = (auth.AUTH_SESSION_INFO_DEFAULT_GROUPS |
+             auth.AUTH_SESSION_INFO_AUTHENTICATED |
+             auth.AUTH_SESSION_INFO_SIMPLE_PRIVILEGES)
+
+    session_info = auth.user_session(samdb, lp_ctx=lp, dn=userdn,
+                                     session_info_flags=flags)
+
+    def _setntacl(path):
+        """A helper to reuse args"""
+        return setntacl(
+            lp, path, SYSVOL_ACL, str(domainsid),
+            use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=s4_passdb,
+            service=SYSVOL_SERVICE, session_info=session_info)
+
     # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
     # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
-    setntacl(lp,sysvol, SYSVOL_ACL, str(domainsid), use_ntvfs=use_ntvfs,
-             skip_invalid_chown=True, passdb=s4_passdb,
-             service=SYSVOL_SERVICE)
+    _setntacl(sysvol)
     for root, dirs, files in os.walk(sysvol, topdown=False):
         for name in files:
             if use_ntvfs and canchown:
                 os.chown(os.path.join(root, name), -1, gid)
     for root, dirs, files in os.walk(sysvol, topdown=False):
         for name in files:
             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),
-                     use_ntvfs=use_ntvfs, skip_invalid_chown=True,
-                     passdb=s4_passdb, service=SYSVOL_SERVICE)
+            _setntacl(os.path.join(root, name))
         for name in dirs:
             if use_ntvfs and canchown:
                 os.chown(os.path.join(root, name), -1, gid)
         for name in dirs:
             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),
-                     use_ntvfs=use_ntvfs, skip_invalid_chown=True,
-                     passdb=s4_passdb, service=SYSVOL_SERVICE)
+            _setntacl(os.path.join(root, name))
 
     # Set acls on Policy folder and policies folders
     set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb=s4_passdb)
 
 
     # Set acls on Policy folder and policies folders
     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 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)
 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)
@@ -1625,7 +1751,9 @@ def check_dir_acl(path, acl, lp, domainsid, direct_db_access):
             fsacl = getntacl(lp, os.path.join(root, name),
                              direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
             if fsacl is None:
             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)))
+                raise ProvisioningError('%s ACL on GPO file %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))
             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))
@@ -1634,14 +1762,16 @@ def check_dir_acl(path, acl, lp, domainsid, direct_db_access):
             fsacl = getntacl(lp, os.path.join(root, name),
                              direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
             if fsacl is None:
             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)))
+                raise ProvisioningError('%s ACL on GPO directory %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,
             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):
+                   direct_db_access):
     """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
     folders beneath.
 
     """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
     folders beneath.
 
@@ -1662,20 +1792,20 @@ def check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
     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))
     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)
+    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,
 
     for policy in res:
         acl = ndr_unpack(security.descriptor,
-                         str(policy["nTSecurityDescriptor"])).as_sddl()
+                         policy["nTSecurityDescriptor"][0]).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,
         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):
+                   lp):
     """Set the ACL for the sysvol share and the subfolders
 
     :param samdb: An LDB object on the SAM db
     """Set the ACL for the sysvol share and the subfolders
 
     :param samdb: An LDB object on the SAM db
@@ -1720,7 +1850,7 @@ def checksysvolacl(samdb, netlogon, sysvol, domainsid, dnsdomain, domaindn,
 
         # Check acls on Policy folder and policies folders
         check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
 
         # Check acls on Policy folder and policies folders
         check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
-                direct_db_access)
+                       direct_db_access)
 
 
 def interface_ips_v4(lp):
 
 
 def interface_ips_v4(lp):
@@ -1752,7 +1882,8 @@ 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,
                    invocationid=None, machinepass=None, ntdsguid=None,
                    dns_backend=None, dnspass=None,
                    serverrole=None, dom_for_fun_level=None,
-                   am_rodc=False, lp=None, use_ntvfs=False, skip_sysvolacl=False):
+                   am_rodc=False, lp=None, use_ntvfs=False,
+                   skip_sysvolacl=False, backend_store=None):
     # create/adapt the group policy GUIDs
     # Default GUID for default policy are described at
     # "How Core Group Policy Works"
     # create/adapt the group policy GUIDs
     # Default GUID for default policy are described at
     # "How Core Group Policy Works"
@@ -1777,14 +1908,15 @@ def provision_fill(samdb, secrets_ldb, logger, names, paths,
     samdb.transaction_start()
     try:
         samdb = fill_samdb(samdb, lp, names, logger=logger,
     samdb.transaction_start()
     try:
         samdb = fill_samdb(samdb, lp, names, logger=logger,
-                       schema=schema,
-                       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)
+                           schema=schema,
+                           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,
+                           backend_store=backend_store)
 
         # Set up group policies (domain policy and domain controller
         # policy)
 
         # Set up group policies (domain policy and domain controller
         # policy)
@@ -1808,9 +1940,9 @@ def provision_fill(samdb, secrets_ldb, logger, names, paths,
             logger.info("Setting acl on sysvol skipped")
 
         secretsdb_self_join(secrets_ldb, domain=names.domain,
             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=names.domainsid,
-                machinepass=machinepass, secure_channel_type=SEC_CHAN_BDC)
+                            realm=names.realm, dnsdomain=names.dnsdomain,
+                            netbiosname=names.netbiosname, domainsid=names.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
 
         # Now set up the right msDS-SupportedEncryptionTypes into the DB
         # In future, this might be determined from some configuration
@@ -1820,12 +1952,13 @@ def provision_fill(samdb, secrets_ldb, logger, names, paths,
             msg = ldb.Message(ldb.Dn(samdb,
                                      samdb.searchone("distinguishedName",
                                                      expression="samAccountName=%s$" % names.netbiosname,
             msg = ldb.Message(ldb.Dn(samdb,
                                      samdb.searchone("distinguishedName",
                                                      expression="samAccountName=%s$" % names.netbiosname,
-                                                     scope=ldb.SCOPE_SUBTREE)))
+                                                     scope=ldb.SCOPE_SUBTREE).decode('utf8')))
             msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(
                 elements=kerberos_enctypes, flags=ldb.FLAG_MOD_REPLACE,
                 name="msDS-SupportedEncryptionTypes")
             samdb.modify(msg)
             msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(
                 elements=kerberos_enctypes, flags=ldb.FLAG_MOD_REPLACE,
                 name="msDS-SupportedEncryptionTypes")
             samdb.modify(msg)
-        except ldb.LdbError, (enum, estr):
+        except ldb.LdbError as e:
+            (enum, estr) = e.args
             if enum != ldb.ERR_NO_SUCH_ATTRIBUTE:
                 # It might be that this attribute does not exist in this schema
                 raise
             if enum != ldb.ERR_NO_SUCH_ATTRIBUTE:
                 # It might be that this attribute does not exist in this schema
                 raise
@@ -1833,11 +1966,12 @@ def provision_fill(samdb, secrets_ldb, logger, names, paths,
         setup_ad_dns(samdb, secrets_ldb, names, paths, lp, logger,
                      hostip=hostip, hostip6=hostip6, dns_backend=dns_backend,
                      dnspass=dnspass, os_level=dom_for_fun_level,
         setup_ad_dns(samdb, secrets_ldb, names, paths, lp, logger,
                      hostip=hostip, hostip6=hostip6, dns_backend=dns_backend,
                      dnspass=dnspass, os_level=dom_for_fun_level,
-                     targetdir=targetdir, fill_level=samdb_fill)
+                     targetdir=targetdir, fill_level=samdb_fill,
+                     backend_store=backend_store)
 
         domainguid = samdb.searchone(basedn=samdb.get_default_basedn(),
 
         domainguid = samdb.searchone(basedn=samdb.get_default_basedn(),
-                                     attribute="objectGUID")
-        assert isinstance(domainguid, str)
+                                     attribute="objectGUID").decode('utf8')
+        assert isinstance(domainguid, string_types)
 
     lastProvisionUSNs = get_last_provision_usn(samdb)
     maxUSN = get_max_usn(samdb, str(names.rootdn))
 
     lastProvisionUSNs = get_last_provision_usn(samdb)
     maxUSN = get_max_usn(samdb, str(names.rootdn))
@@ -1848,12 +1982,12 @@ def provision_fill(samdb, secrets_ldb, logger, names, paths,
 
     logger.info("Setting up sam.ldb rootDSE marking as synchronized")
     setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"),
 
     logger.info("Setting up sam.ldb rootDSE marking as synchronized")
     setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"),
-                      { 'NTDSGUID' : names.ntdsguid })
+                      {'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,
 
     # 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)
+                  quiet=True)
     samdb.transaction_start()
     try:
         # a small number of GUIDs are missing because of ordering issues in the
     samdb.transaction_start()
     try:
         # a small number of GUIDs are missing because of ordering issues in the
@@ -1870,7 +2004,7 @@ def provision_fill(samdb, secrets_ldb, logger, names, paths,
                                   'ipsecNegotiationPolicyReference',
                                   'ipsecNFAReference'])
         if chk.check_database(DN=names.schemadn, scope=ldb.SCOPE_SUBTREE,
                                   'ipsecNegotiationPolicyReference',
                                   'ipsecNFAReference'])
         if chk.check_database(DN=names.schemadn, scope=ldb.SCOPE_SUBTREE,
-                attrs=['attributeId', 'governsId']) != 0:
+                              attrs=['attributeId', 'governsId']) != 0:
             raise ProvisioningError("Duplicate attributeId or governsId in schema. Must be fixed manually!!")
     except:
         samdb.transaction_cancel()
             raise ProvisioningError("Duplicate attributeId or governsId in schema. Must be fixed manually!!")
     except:
         samdb.transaction_cancel()
@@ -1891,7 +2025,7 @@ _ROLES_MAP = {
     "member server": "member server",
     "standalone": "standalone server",
     "standalone server": "standalone server",
     "member server": "member server",
     "standalone": "standalone server",
     "standalone server": "standalone server",
-    }
+}
 
 
 def sanitize_server_role(role):
 
 
 def sanitize_server_role(role):
@@ -1909,7 +2043,7 @@ def sanitize_server_role(role):
 
 
 def provision_fake_ypserver(logger, samdb, domaindn, netbiosname, nisdomain,
 
 
 def provision_fake_ypserver(logger, samdb, domaindn, netbiosname, nisdomain,
-        maxuid, maxgid):
+                            maxuid, maxgid):
     """Create AD entries for the fake ypserver.
 
     This is needed for being able to manipulate posix attrs via ADUC.
     """Create AD entries for the fake ypserver.
 
     This is needed for being able to manipulate posix attrs via ADUC.
@@ -1918,10 +2052,10 @@ def provision_fake_ypserver(logger, samdb, domaindn, netbiosname, nisdomain,
     try:
         logger.info("Setting up fake yp server settings")
         setup_add_ldif(samdb, setup_path("ypServ30.ldif"), {
     try:
         logger.info("Setting up fake yp server settings")
         setup_add_ldif(samdb, setup_path("ypServ30.ldif"), {
-        "DOMAINDN": domaindn,
-        "NETBIOSNAME": netbiosname,
-        "NISDOMAIN": nisdomain,
-         })
+            "DOMAINDN": domaindn,
+            "NETBIOSNAME": netbiosname,
+            "NISDOMAIN": nisdomain,
+        })
     except:
         samdb.transaction_cancel()
         raise
     except:
         samdb.transaction_cancel()
         raise
@@ -1929,20 +2063,64 @@ def provision_fake_ypserver(logger, samdb, domaindn, netbiosname, nisdomain,
         samdb.transaction_commit()
 
 
         samdb.transaction_commit()
 
 
+def directory_create_or_exists(path, mode=0o755):
+    if not os.path.exists(path):
+        try:
+            os.mkdir(path, mode)
+        except OSError as e:
+            if e.errno in [errno.EEXIST]:
+                pass
+            else:
+                raise ProvisioningError("Failed to create directory %s: %s" % (path, e.strerror))
+
+
+def determine_host_ip(logger, lp, hostip=None):
+    if hostip is None:
+        logger.info("Looking up IPv4 addresses")
+        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",
+                               hostip)
+    if hostip == "127.0.0.1":
+        hostip = None
+    if hostip is None:
+        logger.warning("No IPv4 address will be assigned")
+
+    return hostip
+
+
+def determine_host_ip6(logger, lp, hostip6=None):
+    if hostip6 is None:
+        logger.info("Looking up IPv6 addresses")
+        hostips = interface_ips_v6(lp)
+        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")
+
+    return hostip6
+
+
 def provision(logger, session_info, smbconf=None,
 def provision(logger, session_info, 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, dns_forwarder=None, dnspass=None,
-        invocationid=None, machinepass=None, ntdsguid=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=None,
-        useeadb=False, am_rodc=False, lp=None, use_ntvfs=False,
-        use_rfc2307=False, maxuid=None, maxgid=None, skip_sysvolacl=True,
-        ldap_backend_forced_uri=None, nosync=False, ldap_dryrun_mode=False, ldap_backend_extra_port=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, dns_forwarder=None, dnspass=None,
+              invocationid=None, machinepass=None, ntdsguid=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=None,
+              useeadb=False, am_rodc=False, lp=None, use_ntvfs=False,
+              use_rfc2307=False, maxuid=None, maxgid=None, skip_sysvolacl=True,
+              ldap_backend_forced_uri=None, nosync=False, ldap_dryrun_mode=False,
+              ldap_backend_extra_port=None, base_schema=None,
+              plaintext_secrets=False, backend_store=None):
     """Provision samba4
 
     :note: caution, this wipes all existing data!
     """Provision samba4
 
     :note: caution, this wipes all existing data!
@@ -1959,6 +2137,8 @@ def provision(logger, session_info, smbconf=None,
 
     if backend_type is None:
         backend_type = "ldb"
 
     if backend_type is None:
         backend_type = "ldb"
+    if backend_store is None:
+        backend_store = get_default_backend_store()
 
     if domainsid is None:
         domainsid = security.random_sid()
 
     if domainsid is None:
         domainsid = security.random_sid()
@@ -2023,38 +2203,17 @@ def provision(logger, session_info, smbconf=None,
         lp = samba.param.LoadParm()
     lp.load(smbconf)
     names = guess_names(lp=lp, hostname=hostname, domain=domain,
         lp = samba.param.LoadParm()
     lp.load(smbconf)
     names = guess_names(lp=lp, hostname=hostname, domain=domain,
-        dnsdomain=realm, serverrole=serverrole, domaindn=domaindn,
-        configdn=configdn, schemadn=schemadn, serverdn=serverdn,
-        sitename=sitename, rootdn=rootdn, domain_names_forced=(samdb_fill == FILL_DRS))
+                        dnsdomain=realm, serverrole=serverrole, domaindn=domaindn,
+                        configdn=configdn, schemadn=schemadn, serverdn=serverdn,
+                        sitename=sitename, rootdn=rootdn, domain_names_forced=(samdb_fill == FILL_DRS))
     paths = provision_paths_from_lp(lp, names.dnsdomain)
 
     paths.bind_gid = bind_gid
     paths = provision_paths_from_lp(lp, names.dnsdomain)
 
     paths.bind_gid = bind_gid
-    paths.root_uid = root_uid;
+    paths.root_uid = root_uid
     paths.root_gid = root_gid
 
     paths.root_gid = root_gid
 
-    if hostip is None:
-        logger.info("Looking up IPv4 addresses")
-        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",
-                    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)
-        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")
-
+    hostip = determine_host_ip(logger, lp, hostip)
+    hostip6 = determine_host_ip6(logger, lp, hostip6)
     names.hostip = hostip
     names.hostip6 = hostip6
     names.domainguid = domainguid
     names.hostip = hostip
     names.hostip6 = hostip6
     names.domainguid = domainguid
@@ -2064,48 +2223,41 @@ def provision(logger, session_info, smbconf=None,
     if serverrole is None:
         serverrole = lp.get("server role")
 
     if serverrole is None:
         serverrole = lp.get("server role")
 
-    if not os.path.exists(paths.private_dir):
-        os.mkdir(paths.private_dir)
-    if not os.path.exists(os.path.join(paths.private_dir, "tls")):
-        os.makedirs(os.path.join(paths.private_dir, "tls"), 0700)
-    if not os.path.exists(paths.state_dir):
-        os.mkdir(paths.state_dir)
+    directory_create_or_exists(paths.private_dir, 0o700)
+    directory_create_or_exists(paths.binddns_dir, 0o770)
+    directory_create_or_exists(os.path.join(paths.private_dir, "tls"))
+    directory_create_or_exists(paths.state_dir)
+    if not plaintext_secrets:
+        setup_encrypted_secrets_key(paths.encrypted_secrets_key_path)
 
     if paths.sysvol and not os.path.exists(paths.sysvol):
 
     if paths.sysvol and not os.path.exists(paths.sysvol):
-        os.makedirs(paths.sysvol, 0775)
+        os.makedirs(paths.sysvol, 0o775)
 
 
-    ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
+    ldapi_url = "ldapi://%s" % urllib_quote(paths.s4_ldapi_path, safe="")
 
     schema = Schema(domainsid, invocationid=invocationid,
 
     schema = Schema(domainsid, invocationid=invocationid,
-        schemadn=names.schemadn)
+                    schemadn=names.schemadn, base_schema=base_schema)
 
     if backend_type == "ldb":
         provision_backend = LDBBackend(backend_type, paths=paths,
 
     if backend_type == "ldb":
         provision_backend = LDBBackend(backend_type, paths=paths,
-            lp=lp,
-            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
-        provision_backend = ExistingBackend(backend_type, paths=paths,
-            lp=lp,
-            names=names, logger=logger,
-            ldap_backend_forced_uri=ldap_backend_forced_uri)
+                                       lp=lp,
+                                       names=names, logger=logger)
     elif backend_type == "fedora-ds":
         provision_backend = FDSBackend(backend_type, paths=paths,
     elif backend_type == "fedora-ds":
         provision_backend = FDSBackend(backend_type, paths=paths,
-            lp=lp,
-            names=names, logger=logger, domainsid=domainsid,
-            schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
-            slapd_path=slapd_path,
-            root=root)
+                                       lp=lp,
+                                       names=names, logger=logger, domainsid=domainsid,
+                                       schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
+                                       slapd_path=slapd_path,
+                                       root=root)
     elif backend_type == "openldap":
         provision_backend = OpenLDAPBackend(backend_type, paths=paths,
     elif backend_type == "openldap":
         provision_backend = OpenLDAPBackend(backend_type, paths=paths,
-            lp=lp,
-            names=names, logger=logger, domainsid=domainsid,
-            schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
-            slapd_path=slapd_path, ol_mmr_urls=ol_mmr_urls,
-            ldap_backend_extra_port=ldap_backend_extra_port,
-            ldap_dryrun_mode=ldap_dryrun_mode, nosync=nosync,
-            ldap_backend_forced_uri=ldap_backend_forced_uri)
+                                            lp=lp,
+                                            names=names, logger=logger, domainsid=domainsid,
+                                            schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
+                                            slapd_path=slapd_path, ol_mmr_urls=ol_mmr_urls,
+                                            ldap_backend_extra_port=ldap_backend_extra_port,
+                                            ldap_dryrun_mode=ldap_dryrun_mode, nosync=nosync,
+                                            ldap_backend_forced_uri=ldap_backend_forced_uri)
     else:
         raise ValueError("Unknown LDAP backend type selected")
 
     else:
         raise ValueError("Unknown LDAP backend type selected")
 
@@ -2120,8 +2272,8 @@ def provision(logger, session_info, smbconf=None,
 
     logger.info("Setting up secrets.ldb")
     secrets_ldb = setup_secretsdb(paths,
 
     logger.info("Setting up secrets.ldb")
     secrets_ldb = setup_secretsdb(paths,
-        session_info=session_info,
-        backend_credentials=provision_backend.credentials, lp=lp)
+                                  session_info=session_info,
+                                  backend_credentials=provision_backend.credentials, lp=lp)
 
     try:
         logger.info("Setting up the registry")
 
     try:
         logger.info("Setting up the registry")
@@ -2141,7 +2293,9 @@ def provision(logger, session_info, smbconf=None,
         samdb = setup_samdb(paths.samdb, session_info,
                             provision_backend, lp, names, logger=logger,
                             serverrole=serverrole,
         samdb = setup_samdb(paths.samdb, session_info,
                             provision_backend, lp, names, logger=logger,
                             serverrole=serverrole,
-                            schema=schema, fill=samdb_fill, am_rodc=am_rodc)
+                            schema=schema, fill=samdb_fill, am_rodc=am_rodc,
+                            plaintext_secrets=plaintext_secrets,
+                            backend_store=backend_store)
 
         if serverrole == "active directory domain controller":
             if paths.netlogon is None:
 
         if serverrole == "active directory domain controller":
             if paths.netlogon is None:
@@ -2151,28 +2305,30 @@ def provision(logger, session_info, smbconf=None,
                 raise MissingShareError("sysvol", paths.smbconf)
 
             if not os.path.isdir(paths.netlogon):
                 raise MissingShareError("sysvol", paths.smbconf)
 
             if not os.path.isdir(paths.netlogon):
-                os.makedirs(paths.netlogon, 0755)
+                os.makedirs(paths.netlogon, 0o755)
 
         if adminpass is None:
             adminpass = samba.generate_random_password(12, 32)
             adminpass_generated = True
         else:
 
         if adminpass is None:
             adminpass = samba.generate_random_password(12, 32)
             adminpass_generated = True
         else:
-            adminpass = unicode(adminpass, 'utf-8')
+            if isinstance(adminpass, binary_type):
+                adminpass = adminpass.decode('utf-8')
             adminpass_generated = False
 
         if samdb_fill == FILL_FULL:
             provision_fill(samdb, secrets_ldb, logger, names, paths,
             adminpass_generated = False
 
         if samdb_fill == FILL_FULL:
             provision_fill(samdb, secrets_ldb, logger, names, paths,
-                    schema=schema, targetdir=targetdir, samdb_fill=samdb_fill,
-                    hostip=hostip, hostip6=hostip6,
-                    next_rid=next_rid, dc_rid=dc_rid, adminpass=adminpass,
-                    krbtgtpass=krbtgtpass,
-                    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, use_ntvfs=use_ntvfs,
-                           skip_sysvolacl=skip_sysvolacl)
+                           schema=schema, targetdir=targetdir, samdb_fill=samdb_fill,
+                           hostip=hostip, hostip6=hostip6,
+                           next_rid=next_rid, dc_rid=dc_rid, adminpass=adminpass,
+                           krbtgtpass=krbtgtpass,
+                           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, use_ntvfs=use_ntvfs,
+                           skip_sysvolacl=skip_sysvolacl,
+                           backend_store=backend_store)
 
         if not is_heimdal_built():
             create_kdc_conf(paths.kdcconf, realm, domain, os.path.dirname(lp.get("log file")))
 
         if not is_heimdal_built():
             create_kdc_conf(paths.kdcconf, realm, domain, os.path.dirname(lp.get("log file")))
@@ -2184,6 +2340,9 @@ def provision(logger, session_info, smbconf=None,
                          realm=names.realm)
         logger.info("A Kerberos configuration suitable for Samba AD has been "
                     "generated at %s", paths.krb5conf)
                          realm=names.realm)
         logger.info("A Kerberos configuration suitable for Samba AD has been "
                     "generated at %s", paths.krb5conf)
+        logger.info("Merge the contents of this file with your system "
+                    "krb5.conf or replace it with this one. Do not create a "
+                    "symlink!")
 
         if serverrole == "active directory domain controller":
             create_dns_update_list(lp, logger, paths)
 
         if serverrole == "active directory domain controller":
             create_dns_update_list(lp, logger, paths)
@@ -2198,16 +2357,42 @@ def provision(logger, session_info, smbconf=None,
     # Now commit the secrets.ldb to disk
     secrets_ldb.transaction_commit()
 
     # Now commit the secrets.ldb to disk
     secrets_ldb.transaction_commit()
 
-    # the commit creates the dns.keytab, now chown it
-    dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
-    if os.path.isfile(dns_keytab_path) and paths.bind_gid is not None:
+    # the commit creates the dns.keytab in the private directory
+    private_dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
+    bind_dns_keytab_path = os.path.join(paths.binddns_dir, paths.dns_keytab)
+
+    if os.path.isfile(private_dns_keytab_path):
+        if os.path.isfile(bind_dns_keytab_path):
+            try:
+                os.unlink(bind_dns_keytab_path)
+            except OSError as e:
+                logger.error("Failed to remove %s: %s" %
+                             (bind_dns_keytab_path, e.strerror))
+
+        # link the dns.keytab to the bind-dns directory
         try:
         try:
-            os.chmod(dns_keytab_path, 0640)
-            os.chown(dns_keytab_path, -1, paths.bind_gid)
-        except OSError:
-            if not os.environ.has_key('SAMBA_SELFTEST'):
-                logger.info("Failed to chown %s to bind gid %u",
-                            dns_keytab_path, paths.bind_gid)
+            os.link(private_dns_keytab_path, bind_dns_keytab_path)
+        except OSError as e:
+            logger.error("Failed to create link %s -> %s: %s" %
+                         (private_dns_keytab_path, bind_dns_keytab_path, e.strerror))
+
+        # chown the dns.keytab in the bind-dns directory
+        if paths.bind_gid is not None:
+            try:
+                os.chmod(paths.binddns_dir, 0o770)
+                os.chown(paths.binddns_dir, -1, paths.bind_gid)
+            except OSError:
+                if 'SAMBA_SELFTEST' not in os.environ:
+                    logger.info("Failed to chown %s to bind gid %u",
+                                paths.binddns_dir, paths.bind_gid)
+
+            try:
+                os.chmod(bind_dns_keytab_path, 0o640)
+                os.chown(bind_dns_keytab_path, -1, paths.bind_gid)
+            except OSError:
+                if 'SAMBA_SELFTEST' not in os.environ:
+                    logger.info("Failed to chown %s to bind gid %u",
+                                bind_dns_keytab_path, paths.bind_gid)
 
     result = ProvisionResult()
     result.server_role = serverrole
 
     result = ProvisionResult()
     result.server_role = serverrole
@@ -2230,33 +2415,33 @@ def provision(logger, session_info, smbconf=None,
 
     if use_rfc2307:
         provision_fake_ypserver(logger=logger, samdb=samdb,
 
     if use_rfc2307:
         provision_fake_ypserver(logger=logger, samdb=samdb,
-                domaindn=names.domaindn, netbiosname=names.netbiosname,
-                nisdomain=names.domain.lower(), maxuid=maxuid, maxgid=maxgid)
+                                domaindn=names.domaindn, netbiosname=names.netbiosname,
+                                nisdomain=names.domain.lower(), maxuid=maxuid, maxgid=maxgid)
 
     return result
 
 
 def provision_become_dc(smbconf=None, targetdir=None,
 
     return result
 
 
 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,
-        dns_backend=None, root=None, nobody=None, users=None,
-        backup=None, serverrole=None, ldap_backend=None,
-        ldap_backend_type=None, sitename=None, debuglevel=1, use_ntvfs=False):
+                        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,
+                        dns_backend=None, root=None, nobody=None, users=None,
+                        backup=None, serverrole=None, ldap_backend=None,
+                        ldap_backend_type=None, sitename=None, debuglevel=1, use_ntvfs=False):
 
     logger = logging.getLogger("provision")
     samba.set_debug_level(debuglevel)
 
     res = provision(logger, system_session(),
 
     logger = logging.getLogger("provision")
     samba.set_debug_level(debuglevel)
 
     res = provision(logger, system_session(),
-        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=None, domainsid=domainsid,
-        machinepass=machinepass,
-        serverrole="active directory domain controller",
-        sitename=sitename, dns_backend=dns_backend, dnspass=dnspass,
-        use_ntvfs=use_ntvfs)
+                    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=None, domainsid=domainsid,
+                    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
 
     res.lp.set("debuglevel", str(debuglevel))
     return res
 
@@ -2273,7 +2458,7 @@ def create_krb5_conf(path, dnsdomain, hostname, realm):
             "DNSDOMAIN": dnsdomain,
             "HOSTNAME": hostname,
             "REALM": realm,
             "DNSDOMAIN": dnsdomain,
             "HOSTNAME": hostname,
             "REALM": realm,
-        })
+    })
 
 
 class ProvisioningError(Exception):
 
 
 class ProvisioningError(Exception):