-#
+
# Unix SMB/CIFS implementation.
# backend code for provisioning a Samba4 server
-# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2008
+# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2010
# Copyright (C) Andrew Bartlett <abartlet@samba.org> 2008-2009
# Copyright (C) Oliver Liebel <oliver@itc.li> 2008-2009
#
from base64 import b64encode
import os
+import re
import pwd
import grp
+import logging
import time
import uuid
import socket
-import param
-import registry
import urllib
import shutil
import ldb
from samba.auth import system_session, admin_session
-from samba import glue, version, Ldb, substitute_var, valid_netbios_name
+import samba
+from samba import version, Ldb, substitute_var, valid_netbios_name
from samba import check_all_substituted, read_and_sub_file, setup_file
-from samba import DS_DOMAIN_FUNCTION_2003, DS_DC_FUNCTION_2008, DS_DC_FUNCTION_2008_R2
+from samba.dsdb import DS_DOMAIN_FUNCTION_2003, DS_DOMAIN_FUNCTION_2008
from samba.dcerpc import security
from samba.dcerpc.misc import SEC_CHAN_BDC, SEC_CHAN_WKSTA
from samba.idmap import IDmapDB
+from samba.ms_display_specifiers import read_ms_ldif
from samba.ntacls import setntacl, dsacl2fsacl
from samba.ndr import ndr_pack,ndr_unpack
+from samba.provisionbackend import (
+ ExistingBackend,
+ FDSBackend,
+ LDBBackend,
+ OpenLDAPBackend,
+ )
+import samba.param
+import samba.registry
from samba.schema import Schema
-from ms_display_specifiers import read_ms_ldif
-from samba.provisionbackend import LDBBackend, ExistingBackend, FDSBackend, OpenLDAPBackend
-from provisionexceptions import ProvisioningError, InvalidNetbiosName
+from samba.samdb import SamDB
__docformat__ = "restructuredText"
def find_setup_dir():
"""Find the setup directory used by provision."""
- dirname = os.path.dirname(__file__)
- if "/site-packages/" in dirname:
- prefix = "/".join(dirname[:dirname.index("/site-packages/")].split("/")[:-2])
+ import sys
+ for prefix in [sys.prefix,
+ os.path.join(os.path.dirname(__file__), "../../../..")]:
for suffix in ["share/setup", "share/samba/setup", "setup"]:
ret = os.path.join(prefix, suffix)
if os.path.isdir(ret):
return ret
# In source tree
+ dirname = os.path.dirname(__file__)
ret = os.path.join(dirname, "../../../setup")
if os.path.isdir(ret):
return ret
# hard coded at this point, but will probably be changed when
# we enable different fsmo roles
+
def get_config_descriptor(domain_sid):
sddl = "O:EAG:EAD:(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
"(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
return ndr_pack(sec)
DEFAULTSITE = "Default-First-Site-Name"
-
-# Exception classes
+LAST_PROVISION_USN_ATTRIBUTE = "lastProvisionUSN"
class ProvisionPaths(object):
+
def __init__(self):
self.shareconf = None
self.hklm = None
self.dns = None
self.winsdb = None
self.private_dir = None
- self.ldapdir = None
- self.slapdconf = None
- self.modulesconf = None
- self.memberofconf = None
- self.olmmron = None
- self.olmmrserveridsconf = None
- self.olmmrsyncreplconf = None
- self.olcdir = None
- self.olslapd = None
- self.olcseedldif = None
class ProvisionNames(object):
+
def __init__(self):
self.rootdn = None
self.domaindn = None
self.hostname = None
self.sitename = None
self.smbconf = None
+
+
+def update_provision_usn(samdb, low, high, replace=False):
+ """Update the field provisionUSN in sam.ldb
+
+ This field is used to track range of USN modified by provision and
+ upgradeprovision.
+ This value is used afterward by next provision to figure out if
+ the field have been modified since last provision.
+
+ :param samdb: An LDB object connect to sam.ldb
+ :param low: The lowest USN modified by this upgrade
+ :param high: The highest USN modified by this upgrade
+ :param replace: A boolean indicating if the range should replace any
+ existing one or appended (default)
+ """
+
+ tab = []
+ if not replace:
+ entry = samdb.search(expression="(&(dn=@PROVISION)(%s=*))" % \
+ LAST_PROVISION_USN_ATTRIBUTE, base="",
+ scope=ldb.SCOPE_SUBTREE,
+ attrs=[LAST_PROVISION_USN_ATTRIBUTE, "dn"])
+ for e in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
+ tab.append(str(e))
+
+ tab.append("%s-%s" % (low, high))
+ 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)
+ samdb.modify(delta)
+
+
+def set_provision_usn(samdb, low, high):
+ """Set the field provisionUSN in sam.ldb
+ This field is used to track range of USN modified by provision and
+ upgradeprovision.
+ This value is used afterward by next provision to figure out if
+ the field have been modified since last provision.
+
+ :param samdb: An LDB object connect to sam.ldb
+ :param low: The lowest USN modified by this upgrade
+ :param high: The highest USN modified by this upgrade"""
+ tab = []
+ tab.append("%s-%s" % (low, high))
+ 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)
+ samdb.add(delta)
+
+
+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
+ :param basedn: A string containing the base DN of 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"])
+ return res[0]["uSNChanged"]
+def get_last_provision_usn(sam):
+ """Get the lastest USN modified by a provision or an upgradeprovision
+
+ :param sam: An LDB object pointing to the sam.ldb
+ :return an integer corresponding to the highest USN modified by
+ (upgrade)provision, 0 is this value is unknown"""
+
+ entry = sam.search(expression="(&(dn=@PROVISION)(%s=*))" % \
+ LAST_PROVISION_USN_ATTRIBUTE,
+ base="", scope=ldb.SCOPE_SUBTREE,
+ attrs=[LAST_PROVISION_USN_ATTRIBUTE])
+ if len(entry):
+ range = []
+ idx = 0
+ p = re.compile(r'-')
+ for r in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
+ tab = p.split(str(r))
+ range.append(tab[0])
+ range.append(tab[1])
+ idx = idx + 1
+ return range
+ else:
+ return None
class ProvisionResult(object):
+
def __init__(self):
self.paths = None
self.domaindn = None
self.lp = None
self.samdb = None
+
def check_install(lp, session_info, credentials):
"""Check whether the current install seems ok.
"""
if lp.get("realm") == "":
raise Exception("Realm empty")
- ldb = Ldb(lp.get("sam database"), session_info=session_info,
+ samdb = Ldb(lp.get("sam database"), session_info=session_info,
credentials=credentials, lp=lp)
- if len(ldb.search("(cn=Administrator)")) != 1:
+ if len(samdb.search("(cn=Administrator)")) != 1:
raise ProvisioningError("No administrator account found")
return nssfn(name)
except KeyError:
pass
- raise KeyError("Unable to find user/group %r" % names)
+ raise KeyError("Unable to find user/group in %r" % names)
findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
"""
assert isinstance(ldif_path, str)
data = read_and_sub_file(ldif_path, subst_vars)
- ldb.add_ldif(data,controls)
+ ldb.add_ldif(data, controls)
def setup_modify_ldif(ldb, ldif_path, subst_vars=None):
:param subst_vars: Optional dictionary with substitution variables.
"""
data = read_and_sub_file(ldif_path, subst_vars)
-
ldb.modify_ldif(data)
except:
ldb.transaction_cancel()
raise
- ldb.transaction_commit()
+ else:
+ ldb.transaction_commit()
def provision_paths_from_lp(lp, dnsdomain):
"""
paths = ProvisionPaths()
paths.private_dir = lp.get("private dir")
+
+ # This is stored without path prefix for the "privateKeytab" attribute in
+ # "secrets_dns.ldif".
paths.dns_keytab = "dns.keytab"
paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
paths.secrets = os.path.join(paths.private_dir, lp.get("secrets database") or "secrets.ldb")
paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
paths.dns = os.path.join(paths.private_dir, "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.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.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
paths.phpldapadminconfig = os.path.join(paths.private_dir,
"phpldapadmin-config.php")
- paths.ldapdir = os.path.join(paths.private_dir,
- "ldap")
- paths.slapdconf = os.path.join(paths.ldapdir,
- "slapd.conf")
- paths.slapdpid = os.path.join(paths.ldapdir,
- "slapd.pid")
- paths.modulesconf = os.path.join(paths.ldapdir,
- "modules.conf")
- paths.memberofconf = os.path.join(paths.ldapdir,
- "memberof.conf")
- paths.olmmrserveridsconf = os.path.join(paths.ldapdir,
- "mmr_serverids.conf")
- paths.olmmrsyncreplconf = os.path.join(paths.ldapdir,
- "mmr_syncrepl.conf")
- paths.olcdir = os.path.join(paths.ldapdir,
- "slapd.d")
- paths.olcseedldif = os.path.join(paths.ldapdir,
- "olc_seed.ldif")
paths.hklm = "hklm.ldb"
paths.hkcr = "hkcr.ldb"
paths.hkcu = "hkcu.ldb"
paths.hku = "hku.ldb"
paths.hkpd = "hkpd.ldb"
paths.hkpt = "hkpt.ldb"
-
paths.sysvol = lp.get("path", "sysvol")
-
paths.netlogon = lp.get("path", "netlogon")
-
paths.smbconf = lp.configfile
-
return paths
if dnsdomain is None:
dnsdomain = lp.get("realm")
if dnsdomain is None or dnsdomain == "":
- raise ProvisioningError("guess_names: 'realm' not specified in supplied smb.conf!")
+ raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp.configfile)
dnsdomain = dnsdomain.lower()
if serverrole is None:
serverrole = lp.get("server role")
if serverrole is None:
- raise ProvisioningError("guess_names: 'server role' not specified in supplied smb.conf!")
+ raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp.configfile)
serverrole = serverrole.lower()
realm = dnsdomain.upper()
+ if lp.get("realm") == "":
+ raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp.configfile)
+
if lp.get("realm").upper() != realm:
- raise ProvisioningError("guess_names: Realm '%s' in smb.conf must match chosen realm '%s'!", lp.get("realm").upper(), realm)
+ raise ProvisioningError("guess_names: 'realm=%s' in %s must match chosen realm '%s'! Please remove the smb.conf file and let provision generate it" % (lp.get("realm").upper(), realm, lp.configfile))
if lp.get("server role").lower() != serverrole:
- raise ProvisioningError("guess_names: server role '%s' in smb.conf must match chosen server role '%s'!", lp.get("server role").upper(), serverrole)
+ raise ProvisioningError("guess_names: 'server role=%s' in %s must match chosen server role '%s'! Please remove the smb.conf file and let provision generate it" % (lp.get("server role").upper(), serverrole, lp.configfile))
if serverrole == "domain controller":
if domain is None:
+ # This will, for better or worse, default to 'WORKGROUP'
domain = lp.get("workgroup")
- if domain is None:
- raise ProvisioningError("guess_names: 'workgroup' not specified in supplied smb.conf!")
domain = domain.upper()
if lp.get("workgroup").upper() != domain:
- raise ProvisioningError("guess_names: Workgroup '%s' in smb.conf must match chosen domain '%s'!", lp.get("workgroup").upper(), domain)
+ raise ProvisioningError("guess_names: Workgroup '%s' in %s must match chosen domain '%s'! Please remove the %s file and let provision generate it" % (lp.get("workgroup").upper(), domain, lp.configfile))
if domaindn is None:
domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
raise InvalidNetbiosName(domain)
if hostname.upper() == realm:
- raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!", realm, hostname)
+ raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm, hostname))
if netbiosname == realm:
- raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!", realm, netbiosname)
+ raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!" % (realm, netbiosname))
if domain == realm:
- raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!", realm, domain)
+ raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
if rootdn is None:
rootdn = domaindn
def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
- targetdir, sid_generator,eadb):
+ targetdir, sid_generator="internal", eadb=False):
"""Create a new smb.conf file based on a couple of basic settings.
"""
assert smbconf is not None
assert realm is not None
realm = realm.upper()
- default_lp = param.LoadParm()
+ default_lp = samba.param.LoadParm()
#Load non-existant file
if os.path.exists(smbconf):
default_lp.load(smbconf)
privdir = os.path.join(targetdir, "private")
else:
privdir = default_lp.get("private dir")
- posixeadb_line = "posix:eadb = " + os.path.abspath(os.path.join(privdir,"eadb.tdb"))
+ posixeadb_line = "posix:eadb = " + os.path.abspath(os.path.join(privdir, "eadb.tdb"))
else:
posixeadb_line = ""
"SIDGENERATOR_LINE": sid_generator_line,
"PRIVATEDIR_LINE": privatedir_line,
"LOCKDIR_LINE": lockdir_line,
- "POSIXEADB_LINE": posixeadb_line
+ "POSIXEADB_LINE": posixeadb_line
})
:param nobody_uid: uid of the UNIX nobody user.
:param users_gid: gid of the UNIX users group.
:param wheel_gid: gid of the UNIX wheel group."""
-
idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
-def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
- provision_backend, names, schema,
- serverrole,
+
+def setup_samdb_partitions(samdb_path, setup_path, logger, lp, session_info,
+ provision_backend, names, schema, serverrole,
erase=False):
"""Setup the partitions for the SAM database.
"""
assert session_info is not None
- old_partitions = None
- new_partitions = None
-
# We use options=["modules:"] to stop the modules loading - we
# just want to wipe and re-initialise the database, not start it up
samdb.transaction_start()
try:
- message("Setting up sam.ldb partitions and settings")
+ logger.info("Setting up sam.ldb partitions and settings")
setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
"SCHEMADN": ldb.Dn(schema.ldb, names.schemadn).get_casefold(),
"CONFIGDN": ldb.Dn(schema.ldb, names.configdn).get_casefold(),
"SERVER_ROLE": serverrole
})
- message("Setting up sam.ldb rootDSE")
+ logger.info("Setting up sam.ldb rootDSE")
setup_samdb_rootdse(samdb, setup_path, names)
-
except:
samdb.transaction_cancel()
raise
-
- samdb.transaction_commit()
+ else:
+ samdb.transaction_commit()
def secretsdb_self_join(secretsdb, domain,
"privateKeytab"]
- msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain));
+ msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
msg["secureChannelType"] = str(secure_channel_type)
msg["flatname"] = [domain]
msg["objectClass"] = ["top", "primaryDomain"]
msg["realm"] = realm
msg["saltPrincipal"] = "host/%s.%s@%s" % (netbiosname.lower(), dnsdomain.lower(), realm.upper())
msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
- msg["privateKeytab"] = ["secrets.keytab"];
+ msg["privateKeytab"] = ["secrets.keytab"]
msg["secret"] = [machinepass]
def setup_secretsdb(path, setup_path, session_info, backend_credentials, lp):
"""Setup the secrets database.
+ :note: This function does not handle exceptions and transaction on purpose,
+ it's up to the caller to do this job.
+
:param path: Path to the secrets database.
:param setup_path: Get the path to a setup file.
:param session_info: Session info.
secrets_ldb = Ldb(path, session_info=session_info,
lp=lp)
secrets_ldb.transaction_start()
- secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
-
- if backend_credentials is not None and 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())
- })
- 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())
- })
-
- return secrets_ldb
+ try:
+ secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
+
+ if backend_credentials is not None and 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())
+ })
+ 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())
+ })
+
+ return secrets_ldb
+ except:
+ secrets_ldb.transaction_cancel()
+ raise
def setup_privileges(path, setup_path, session_info, lp):
"""Setup the privileges database.
:param credentials: Credentials
:param lp: Loadparm context
"""
- reg = registry.Registry()
- hive = registry.open_ldb(path, session_info=session_info,
+ reg = samba.registry.Registry()
+ hive = samba.registry.open_ldb(path, session_info=session_info,
lp_ctx=lp)
- reg.mount_hive(hive, registry.HKEY_LOCAL_MACHINE)
+ reg.mount_hive(hive, samba.registry.HKEY_LOCAL_MACHINE)
provision_reg = setup_path("provision.reg")
assert os.path.exists(provision_reg)
reg.diff_apply(provision_reg)
"DOMAINDN": names.domaindn})
# add the NTDSGUID based SPNs
- ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
+ ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=%s,CN=Sites,CN=Configuration,%s" % (names.hostname, names.sitename, names.domaindn)
names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID",
expression="", scope=ldb.SCOPE_BASE)
assert isinstance(names.ntdsguid, str)
"DNSPASS_B64": b64encode(dnspass),
})
+def getpolicypath(sysvolpath, dnsdomain, guid):
+ if guid[0] != "{":
+ guid = "{%s}" % guid
+ policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
+ return policy_path
-def setup_gpo(paths,names,samdb,policyguid,policyguid_dc,domainsid):
- policy_path = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
- "{" + policyguid + "}")
+def create_gpo_struct(policy_path):
os.makedirs(policy_path, 0755)
open(os.path.join(policy_path, "GPT.INI"), 'w').write(
"[General]\r\nVersion=65543")
os.makedirs(os.path.join(policy_path, "MACHINE"), 0755)
os.makedirs(os.path.join(policy_path, "USER"), 0755)
- policy_path_dc = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
- "{" + policyguid_dc + "}")
- os.makedirs(policy_path_dc, 0755)
- open(os.path.join(policy_path_dc, "GPT.INI"), 'w').write(
- "[General]\r\nVersion=2")
- os.makedirs(os.path.join(policy_path_dc, "MACHINE"), 0755)
- os.makedirs(os.path.join(policy_path_dc, "USER"), 0755)
-
-
-def setup_samdb(path, setup_path, session_info, provision_backend, lp,
- names, message,
- domainsid, domainguid, policyguid, policyguid_dc,
- fill, adminpass, krbtgtpass,
- machinepass, invocationid, dnspass, ntdsguid,
- serverrole, dom_for_fun_level=None,
- schema=None):
+
+def setup_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
+ policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
+ create_gpo_struct(policy_path)
+
+ policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
+ create_gpo_struct(policy_path)
+
+
+def setup_samdb(path, setup_path, session_info, provision_backend, lp, names,
+ logger, domainsid, domainguid, policyguid, policyguid_dc, fill,
+ adminpass, krbtgtpass, machinepass, invocationid, dnspass, ntdsguid,
+ serverrole, am_rodc=False, dom_for_fun_level=None, schema=None):
"""Setup a complete SAM Database.
:note: This will wipe the main SAM database file!
# ATTENTION: Do NOT change these default values without discussion with the
# team and/or release manager. They have a big impact on the whole program!
- domainControllerFunctionality = DS_DC_FUNCTION_2008
+ domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008
if dom_for_fun_level is None:
dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
- if dom_for_fun_level < DS_DOMAIN_FUNCTION_2003:
- message("You want to run SAMBA 4 on a domain and forest function level lower than Windows 2003 (Native). This is not recommended")
if dom_for_fun_level > domainControllerFunctionality:
raise ProvisioningError("You want to run SAMBA 4 on a domain and forest function level which itself is higher than its actual DC function level (2008). This won't work!")
forestFunctionality = dom_for_fun_level
# Also wipes the database
- setup_samdb_partitions(path, setup_path, message=message, lp=lp,
- provision_backend=provision_backend, session_info=session_info,
- names=names,
- serverrole=serverrole, schema=schema)
+ setup_samdb_partitions(path, setup_path, logger=logger, lp=lp,
+ provision_backend=provision_backend, session_info=session_info,
+ names=names, serverrole=serverrole, schema=schema)
- if (schema == None):
- schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn)
+ if schema is None:
+ schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn,
+ am_rodc=am_rodc)
- # Load the database, but importantly, use Ldb not SamDB as we don't want to load the global schema
- samdb = Ldb(session_info=session_info,
- credentials=provision_backend.credentials, lp=lp)
+ # Load the database, but don's load the global schema and don't connect quite yet
+ samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
+ credentials=provision_backend.credentials, lp=lp, global_schema=False,
+ am_rodc=am_rodc)
- message("Pre-loading the Samba 4 and AD schema")
+ logger.info("Pre-loading the Samba 4 and AD schema")
# Load the schema from the one we computed earlier
- samdb.set_schema_from_ldb(schema.ldb)
+ samdb.set_schema(schema)
# And now we can connect to the DB - the schema won't be loaded from the DB
samdb.connect(path)
samdb.set_domain_sid(str(domainsid))
samdb.set_invocation_id(invocationid)
+ samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
- message("Adding DomainDN: %s" % names.domaindn)
+ logger.info("Adding DomainDN: %s" % names.domaindn)
#impersonate domain admin
admin_session_info = admin_session(lp, str(domainsid))
"SAMBA_VERSION_STRING": version
})
- message("Adding configuration container")
+ logger.info("Adding configuration container")
descr = b64encode(get_config_descriptor(domainsid))
setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
"CONFIGDN": names.configdn,
})
# The LDIF here was created when the Schema object was constructed
- message("Setting up sam.ldb schema")
+ logger.info("Setting up sam.ldb schema")
samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
samdb.modify_ldif(schema.schema_dn_modify)
samdb.write_prefixes_from_schema()
setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
{"SCHEMADN": names.schemadn})
- message("Reopening sam.ldb with new schema");
+ logger.info("Reopening sam.ldb with new schema")
+ except:
+ samdb.transaction_cancel()
+ raise
+ else:
samdb.transaction_commit()
- samdb = Ldb(session_info=admin_session_info,
- credentials=provision_backend.credentials, lp=lp)
- samdb.connect(path)
- samdb.transaction_start()
- samdb.set_invocation_id(invocationid)
- message("Setting up sam.ldb configuration data")
+ samdb = SamDB(session_info=admin_session_info,
+ credentials=provision_backend.credentials, lp=lp,
+ global_schema=False, am_rodc=am_rodc)
+ samdb.connect(path)
+ samdb.transaction_start()
+ try:
+ samdb.invocation_id = invocationid
+
+ logger.info("Setting up sam.ldb configuration data")
setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
"CONFIGDN": names.configdn,
"NETBIOSNAME": names.netbiosname,
"SCHEMADN": names.schemadn,
"DOMAINDN": names.domaindn,
"SERVERDN": names.serverdn,
- "FOREST_FUNCTIONALALITY": str(forestFunctionality)
+ "FOREST_FUNCTIONALITY": str(forestFunctionality),
+ "DOMAIN_FUNCTIONALITY": str(domainFunctionality)
})
- message("Setting up display specifiers")
+ logger.info("Setting up display specifiers")
display_specifiers_ldif = read_ms_ldif(setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
display_specifiers_ldif = substitute_var(display_specifiers_ldif, {"CONFIGDN": names.configdn})
check_all_substituted(display_specifiers_ldif)
samdb.add_ldif(display_specifiers_ldif)
- message("Adding users container")
+ logger.info("Adding users container")
setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
"DOMAINDN": names.domaindn})
- message("Modifying users container")
+ logger.info("Modifying users container")
setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
"DOMAINDN": names.domaindn})
- message("Adding computers container")
+ logger.info("Adding computers container")
setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
"DOMAINDN": names.domaindn})
- message("Modifying computers container")
+ logger.info("Modifying computers container")
setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
"DOMAINDN": names.domaindn})
- message("Setting up sam.ldb data")
+ logger.info("Setting up sam.ldb data")
setup_add_ldif(samdb, setup_path("provision.ldif"), {
"CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
"DOMAINDN": names.domaindn,
"CONFIGDN": names.configdn,
"SCHEMADN": names.schemadn})
if fill == FILL_FULL:
- message("Setting up sam.ldb users and groups")
+ logger.info("Setting up sam.ldb users and groups")
setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
"DOMAINDN": names.domaindn,
"DOMAINSID": str(domainsid),
"KRBTGTPASS_B64": b64encode(krbtgtpass),
})
- message("Setting up self join")
+ logger.info("Setting up self join")
setup_self_join(samdb, names=names, invocationid=invocationid,
dnspass=dnspass,
machinepass=machinepass,
domainControllerFunctionality=domainControllerFunctionality,
ntdsguid=ntdsguid)
- ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
+ ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=%s,CN=Sites,CN=Configuration,%s" % (names.hostname, names.sitename, names.domaindn)
names.ntdsguid = samdb.searchone(basedn=ntds_dn,
attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
assert isinstance(names.ntdsguid, str)
-
except:
samdb.transaction_cancel()
raise
-
- samdb.transaction_commit()
- return samdb
+ else:
+ samdb.transaction_commit()
+ return samdb
FILL_FULL = "FULL"
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)"
-def set_gpo_acl(path,acl,lp,domsid):
- setntacl(lp,path,acl,domsid)
- for root, dirs, files in os.walk(path, topdown=False):
- for name in files:
- setntacl(lp,os.path.join(root, name),acl,domsid)
- for name in dirs:
- setntacl(lp,os.path.join(root, name),acl,domsid)
-
-def setsysvolacl(samdb,names,netlogon,sysvol,gid,domainsid,lp):
- canchown = 1
- try:
- os.chown(sysvol,-1,gid)
- except:
- canchown = 0
-
- setntacl(lp,sysvol,SYSVOL_ACL,str(domainsid))
- for root, dirs, files in os.walk(sysvol, topdown=False):
- for name in files:
- if canchown:
- os.chown(os.path.join(root, name),-1,gid)
- setntacl(lp,os.path.join(root, name),SYSVOL_ACL,str(domainsid))
- for name in dirs:
- if canchown:
- os.chown(os.path.join(root, name),-1,gid)
- setntacl(lp,os.path.join(root, name),SYSVOL_ACL,str(domainsid))
-
- # Set ACL for GPO
- policy_path = os.path.join(sysvol, names.dnsdomain, "Policies")
- set_gpo_acl(policy_path,dsacl2fsacl(POLICIES_ACL,str(domainsid)),lp,str(domainsid))
- res = samdb.search(base="CN=Policies,CN=System,%s"%(names.domaindn),
- attrs=["cn","nTSecurityDescriptor"],
- expression="", scope=ldb.SCOPE_ONELEVEL)
- for policy in res:
- acl = ndr_unpack(security.descriptor,str(policy["nTSecurityDescriptor"])).as_sddl()
- policy_path = os.path.join(sysvol, names.dnsdomain, "Policies",
- str(policy["cn"]))
- set_gpo_acl(policy_path,dsacl2fsacl(acl,str(domainsid)),lp,str(domainsid))
-
-
-
-def provision(setup_dir, message, session_info,
+def set_dir_acl(path, acl, lp, domsid):
+ setntacl(lp, path, acl, domsid)
+ for root, dirs, files in os.walk(path, topdown=False):
+ for name in files:
+ setntacl(lp, os.path.join(root, name), acl, domsid)
+ for name in dirs:
+ setntacl(lp, os.path.join(root, name), acl, domsid)
+
+
+def set_gpo_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp):
+ # Set ACL for GPO
+ policy_path = os.path.join(sysvol, dnsdomain, "Policies")
+ set_dir_acl(policy_path,dsacl2fsacl(POLICIES_ACL, str(domainsid)),
+ lp, str(domainsid))
+ res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
+ attrs=["cn", "nTSecurityDescriptor"],
+ expression="", scope=ldb.SCOPE_ONELEVEL)
+ for policy in res:
+ acl = ndr_unpack(security.descriptor,
+ str(policy["nTSecurityDescriptor"])).as_sddl()
+ policy_path = getpolicypath(sysvol,dnsdomain,str(policy["cn"]))
+ set_dir_acl(policy_path, dsacl2fsacl(acl, str(domainsid)), lp,
+ str(domainsid))
+
+def setsysvolacl(samdb, netlogon, sysvol, gid, domainsid, dnsdomain, domaindn,
+ lp):
+ try:
+ os.chown(sysvol,-1,gid)
+ except:
+ canchown = False
+ else:
+ canchown = True
+
+ setntacl(lp,sysvol,SYSVOL_ACL,str(domainsid))
+ for root, dirs, files in os.walk(sysvol, topdown=False):
+ for name in files:
+ if canchown:
+ os.chown(os.path.join(root, name),-1,gid)
+ setntacl(lp,os.path.join(root, name),SYSVOL_ACL,str(domainsid))
+ for name in dirs:
+ if canchown:
+ os.chown(os.path.join(root, name),-1,gid)
+ setntacl(lp,os.path.join(root, name),SYSVOL_ACL,str(domainsid))
+ set_gpo_acl(sysvol,dnsdomain,domainsid,domaindn,samdb,lp)
+
+
+def provision(setup_dir, logger, session_info,
credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL,
realm=None,
rootdn=None, domaindn=None, schemadn=None, configdn=None,
sitename=None,
ol_mmr_urls=None, ol_olc=None,
setup_ds_path=None, slapd_path=None, nosync=False,
- ldap_dryrun_mode=False,useeadb=False):
+ ldap_dryrun_mode=False, useeadb=False, am_rodc=False):
"""Provision samba4
:note: caution, this wipes all existing data!
policyguid_dc = policyguid_dc.upper()
if adminpass is None:
- adminpass = glue.generate_random_str(12)
+ adminpass = samba.generate_random_password(12, 32)
if krbtgtpass is None:
- krbtgtpass = glue.generate_random_str(12)
+ krbtgtpass = samba.generate_random_password(128, 255)
if machinepass is None:
- machinepass = glue.generate_random_str(12)
+ machinepass = samba.generate_random_password(128, 255)
if dnspass is None:
- dnspass = glue.generate_random_str(12)
+ dnspass = samba.generate_random_password(128, 255)
if ldapadminpass is None:
#Make a new, random password between Samba and it's LDAP server
- ldapadminpass=glue.generate_random_str(12)
+ ldapadminpass=samba.generate_random_password(128, 255)
if backend_type is None:
backend_type = "ldb"
root_uid = findnss_uid([root or "root"])
nobody_uid = findnss_uid([nobody or "nobody"])
- users_gid = findnss_gid([users or "users"])
+ users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
if wheel is None:
wheel_gid = findnss_gid(["wheel", "adm"])
else:
bind_gid = None
if targetdir is not None:
- if (not os.path.exists(os.path.join(targetdir, "etc"))):
- os.makedirs(os.path.join(targetdir, "etc"))
smbconf = os.path.join(targetdir, "etc", "smb.conf")
elif smbconf is None:
- smbconf = param.default_path()
+ smbconf = samba.param.default_path()
+ if not os.path.exists(os.path.dirname(smbconf)):
+ os.makedirs(os.path.dirname(smbconf))
# only install a new smb.conf if there isn't one there already
if os.path.exists(smbconf):
data = open(smbconf, 'r').read()
data = data.lstrip()
if data is None or data == "":
- make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
- targetdir, sid_generator, useeadb)
+ make_smbconf(smbconf, setup_path, hostname, domain, realm,
+ serverrole, targetdir, sid_generator, useeadb)
else:
make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
targetdir, sid_generator, useeadb)
- lp = param.LoadParm()
+ lp = samba.param.LoadParm()
lp.load(smbconf)
names = guess_names(lp=lp, hostname=hostname, domain=domain,
paths.bind_gid = bind_gid
if hostip is None:
- try:
- hostip = socket.getaddrinfo(names.hostname, None, socket.AF_INET, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
- except socket.gaierror, (socket.EAI_NODATA, msg):
- hostip = None
+ hostips = samba.interface_ips(lp, False)
+ if len(hostips) == 0:
+ logger.warning("No external IPv4 address has been found. Using loopback.")
+ hostip = '127.0.0.1'
+ else:
+ hostip = hostips[0]
+ if len(hostips) > 1:
+ logger.warning("More than one IPv4 address found. Using %s.", hostip)
if hostip6 is None:
try:
- hostip6 = socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
+ for ip in socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP):
+ if hostip6 is None:
+ hostip6 = ip[-1][0]
+ if hostip6 == '::1' and ip[-1][0] != '::1':
+ hostip6 = ip[-1][0]
except socket.gaierror, (socket.EAI_NODATA, msg):
hostip6 = None
if not os.path.exists(paths.private_dir):
os.mkdir(paths.private_dir)
- if not os.path.exists(os.path.join(paths.private_dir,"tls")):
- os.mkdir(os.path.join(paths.private_dir,"tls"))
+ if not os.path.exists(os.path.join(paths.private_dir, "tls")):
+ os.mkdir(os.path.join(paths.private_dir, "tls"))
ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
- schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn)
-
+ schema = Schema(setup_path, domainsid, invocationid=invocationid, schemadn=names.schemadn,
+ serverdn=names.serverdn, am_rodc=am_rodc)
+
if backend_type == "ldb":
provision_backend = LDBBackend(backend_type,
paths=paths, setup_path=setup_path,
lp=lp, credentials=credentials,
names=names,
- message=message)
+ logger=logger)
elif backend_type == "existing":
provision_backend = ExistingBackend(backend_type,
paths=paths, setup_path=setup_path,
lp=lp, credentials=credentials,
names=names,
- message=message)
+ logger=logger,
+ ldapi_url=ldapi_url)
elif backend_type == "fedora-ds":
provision_backend = FDSBackend(backend_type,
paths=paths, setup_path=setup_path,
lp=lp, credentials=credentials,
names=names,
- message=message,
+ logger=logger,
domainsid=domainsid,
schema=schema,
hostname=hostname,
paths=paths, setup_path=setup_path,
lp=lp, credentials=credentials,
names=names,
- message=message,
+ logger=logger,
domainsid=domainsid,
schema=schema,
hostname=hostname,
ol_mmr_urls=ol_mmr_urls,
nosync=nosync)
else:
- raise ProvisioningError("Unknown LDAP backend type selected")
+ raise ValueError("Unknown LDAP backend type selected")
provision_backend.init()
provision_backend.start()
# only install a new shares config db if there is none
if not os.path.exists(paths.shareconf):
- message("Setting up share.ldb")
+ logger.info("Setting up share.ldb")
share_ldb = Ldb(paths.shareconf, session_info=session_info,
lp=lp)
share_ldb.load_ldif_file_add(setup_path("share.ldif"))
- message("Setting up secrets.ldb")
+ logger.info("Setting up secrets.ldb")
secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
- session_info=session_info,
- backend_credentials=provision_backend.secrets_credentials, lp=lp)
-
- message("Setting up the registry")
- setup_registry(paths.hklm, setup_path, session_info,
- lp=lp)
-
- message("Setting up the privileges database")
- setup_privileges(paths.privilege, setup_path, session_info, lp=lp)
-
- message("Setting up idmap db")
- idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
- lp=lp)
-
- message("Setting up SAM db")
- samdb = setup_samdb(paths.samdb, setup_path, session_info,
- provision_backend, lp, names,
- message,
- domainsid=domainsid,
- schema=schema, domainguid=domainguid,
- policyguid=policyguid, policyguid_dc=policyguid_dc,
- fill=samdb_fill,
- adminpass=adminpass, krbtgtpass=krbtgtpass,
- invocationid=invocationid,
- machinepass=machinepass, dnspass=dnspass,
- ntdsguid=ntdsguid, serverrole=serverrole,
- dom_for_fun_level=dom_for_fun_level)
-
- if serverrole == "domain controller":
- if paths.netlogon is None:
- message("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
- message("Please either remove %s or see the template at %s" %
- ( paths.smbconf, setup_path("provision.smb.conf.dc")))
- assert(paths.netlogon is not None)
-
- if paths.sysvol is None:
- message("Existing smb.conf does not have a [sysvol] share, but you are configuring a DC.")
- message("Please either remove %s or see the template at %s" %
- (paths.smbconf, setup_path("provision.smb.conf.dc")))
- assert(paths.sysvol is not None)
-
-
- if not os.path.isdir(paths.netlogon):
- os.makedirs(paths.netlogon, 0755)
+ session_info=session_info,
+ backend_credentials=provision_backend.secrets_credentials, lp=lp)
- if samdb_fill == FILL_FULL:
- setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
- root_uid=root_uid, nobody_uid=nobody_uid,
- users_gid=users_gid, wheel_gid=wheel_gid)
+ try:
+ logger.info("Setting up the registry")
+ setup_registry(paths.hklm, setup_path, session_info,
+ lp=lp)
- if serverrole == "domain controller":
- # Set up group policies (domain policy and domain controller policy)
- setup_gpo(paths,names,samdb,policyguid,policyguid_dc,domainsid)
- setsysvolacl(samdb,names,paths.netlogon,paths.sysvol,wheel_gid,domainsid,lp)
+ logger.info("Setting up the privileges database")
+ setup_privileges(paths.privilege, setup_path, session_info, lp=lp)
- message("Setting up sam.ldb rootDSE marking as synchronized")
- setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
+ logger.info("Setting up idmap db")
+ idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
+ lp=lp)
- secretsdb_self_join(secrets_ldb, domain=names.domain,
- realm=names.realm,
- dnsdomain=names.dnsdomain,
- netbiosname=names.netbiosname,
+ logger.info("Setting up SAM db")
+ samdb = setup_samdb(paths.samdb, setup_path, session_info,
+ provision_backend, lp, names,
+ logger=logger,
domainsid=domainsid,
- machinepass=machinepass,
- secure_channel_type=SEC_CHAN_BDC)
+ schema=schema, domainguid=domainguid,
+ policyguid=policyguid, policyguid_dc=policyguid_dc,
+ fill=samdb_fill,
+ adminpass=adminpass, krbtgtpass=krbtgtpass,
+ invocationid=invocationid,
+ machinepass=machinepass, dnspass=dnspass,
+ ntdsguid=ntdsguid, serverrole=serverrole,
+ dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc)
if serverrole == "domain controller":
- secretsdb_setup_dns(secrets_ldb, setup_path,
- paths.private_dir,
- realm=names.realm, dnsdomain=names.dnsdomain,
- dns_keytab_path=paths.dns_keytab,
- dnspass=dnspass)
-
- domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
- assert isinstance(domainguid, str)
-
- # Only make a zone file on the first DC, it should be replicated
- # with DNS replication
- create_zone_file(lp, message, paths, targetdir, setup_path, dnsdomain=names.dnsdomain,
- hostip=hostip,
- hostip6=hostip6, hostname=names.hostname,
- realm=names.realm,
- domainguid=domainguid, ntdsguid=names.ntdsguid)
-
- create_named_conf(paths, setup_path, realm=names.realm,
- dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
-
- create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
- dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
- keytab_name=paths.dns_keytab)
- message("See %s for an example configuration include file for BIND" % paths.namedconf)
- message("and %s for further documentation required for secure DNS updates" % paths.namedtxt)
-
- create_krb5_conf(paths.krb5conf, setup_path,
- dnsdomain=names.dnsdomain, hostname=names.hostname,
- realm=names.realm)
- message("A Kerberos configuration suitable for Samba 4 has been generated at %s" % paths.krb5conf)
-
- provision_backend.post_setup()
- provision_backend.shutdown()
-
- create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
- ldapi_url)
+ if paths.netlogon is None:
+ logger.info("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
+ logger.info("Please either remove %s or see the template at %s" %
+ (paths.smbconf, setup_path("provision.smb.conf.dc")))
+ assert paths.netlogon is not None
+
+ if paths.sysvol is None:
+ logger.info("Existing smb.conf does not have a [sysvol] share, but you"
+ " are configuring a DC.")
+ logger.info("Please either remove %s or see the template at %s" %
+ (paths.smbconf, setup_path("provision.smb.conf.dc")))
+ assert paths.sysvol is not None
+
+ if not os.path.isdir(paths.netlogon):
+ os.makedirs(paths.netlogon, 0755)
+
+ if samdb_fill == FILL_FULL:
+ setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
+ root_uid=root_uid, nobody_uid=nobody_uid,
+ users_gid=users_gid, wheel_gid=wheel_gid)
+
+ if serverrole == "domain controller":
+ # Set up group policies (domain policy and domain controller policy)
+ setup_gpo(paths.sysvol, names.dnsdomain, policyguid, policyguid_dc)
+ setsysvolacl(samdb, paths.netlogon, paths.sysvol, wheel_gid,
+ domainsid, names.dnsdomain, names.domaindn, lp)
+
+ logger.info("Setting up sam.ldb rootDSE marking as synchronized")
+ setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
+
+ secretsdb_self_join(secrets_ldb, domain=names.domain,
+ realm=names.realm,
+ dnsdomain=names.dnsdomain,
+ netbiosname=names.netbiosname,
+ domainsid=domainsid,
+ machinepass=machinepass,
+ secure_channel_type=SEC_CHAN_BDC)
+
+ if serverrole == "domain controller":
+ secretsdb_setup_dns(secrets_ldb, setup_path,
+ paths.private_dir,
+ realm=names.realm, dnsdomain=names.dnsdomain,
+ dns_keytab_path=paths.dns_keytab,
+ dnspass=dnspass)
+
+ domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
+ assert isinstance(domainguid, str)
+
+ # Only make a zone file on the first DC, it should be replicated
+ # with DNS replication
+ create_zone_file(lp, logger, paths, targetdir, setup_path,
+ dnsdomain=names.dnsdomain, hostip=hostip, hostip6=hostip6,
+ hostname=names.hostname, realm=names.realm,
+ domainguid=domainguid, ntdsguid=names.ntdsguid)
+
+ create_named_conf(paths, setup_path, realm=names.realm,
+ dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
+
+ create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
+ dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
+ keytab_name=paths.dns_keytab)
+ logger.info("See %s for an example configuration include file for BIND", paths.namedconf)
+ logger.info("and %s for further documentation required for secure DNS "
+ "updates", paths.namedtxt)
+
+ create_krb5_conf(paths.krb5conf, setup_path,
+ dnsdomain=names.dnsdomain, hostname=names.hostname,
+ realm=names.realm)
+ logger.info("A Kerberos configuration suitable for Samba 4 has been "
+ "generated at %s", paths.krb5conf)
+
+ lastProvisionUSNs = get_last_provision_usn(samdb)
+ maxUSN = get_max_usn(samdb, str(names.rootdn))
+ if lastProvisionUSNs is not None:
+ update_provision_usn(samdb, 0, maxUSN, 1)
+ else:
+ set_provision_usn(samdb, 0, maxUSN)
+
+ if serverrole == "domain controller":
+ create_dns_update_list(lp, logger, paths, setup_path)
+
+ provision_backend.post_setup()
+ provision_backend.shutdown()
+
+ create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
+ ldapi_url)
+ except:
+ secrets_ldb.transaction_cancel()
+ raise
#Now commit the secrets.ldb to disk
secrets_ldb.transaction_commit()
os.chmod(dns_keytab_path, 0640)
os.chown(dns_keytab_path, -1, paths.bind_gid)
except OSError:
- message("Failed to chown %s to bind gid %u" % (dns_keytab_path, paths.bind_gid))
+ logger.info("Failed to chown %s to bind gid %u", dns_keytab_path,
+ paths.bind_gid)
- message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
+ logger.info("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php",
+ paths.phpldapadminconfig)
- message("Once the above files are installed, your Samba4 server will be ready to use")
- message("Server Role: %s" % serverrole)
- message("Hostname: %s" % names.hostname)
- message("NetBIOS Domain: %s" % names.domain)
- message("DNS Domain: %s" % names.dnsdomain)
- message("DOMAIN SID: %s" % str(domainsid))
+ logger.info("Once the above files are installed, your Samba4 server will be ready to use")
+ logger.info("Server Role: %s" % serverrole)
+ logger.info("Hostname: %s" % names.hostname)
+ logger.info("NetBIOS Domain: %s" % names.domain)
+ logger.info("DNS Domain: %s" % names.dnsdomain)
+ logger.info("DOMAIN SID: %s" % str(domainsid))
if samdb_fill == FILL_FULL:
- message("Admin password: %s" % adminpass)
+ logger.info("Admin password: %s" % adminpass)
if provision_backend.type is not "ldb":
if provision_backend.credentials.get_bind_dn() is not None:
- message("LDAP Backend Admin DN: %s" % provision_backend.credentials.get_bind_dn())
+ logger.info("LDAP Backend Admin DN: %s" % provision_backend.credentials.get_bind_dn())
else:
- message("LDAP Admin User: %s" % provision_backend.credentials.get_username())
+ logger.info("LDAP Admin User: %s" % provision_backend.credentials.get_username())
- message("LDAP Admin Password: %s" % provision_backend.credentials.get_password())
+ logger.info("LDAP Admin Password: %s" % provision_backend.credentials.get_password())
if provision_backend.slapd_command_escaped is not None:
# now display slapd_command_file.txt to show how slapd must be started next time
- message("Use later the following commandline to start slapd, then Samba:")
- message(provision_backend.slapd_command_escaped)
- message("This slapd-Commandline is also stored under: " + paths.ldapdir + "/ldap_backend_startup.sh")
-
+ logger.info("Use later the following commandline to start slapd, then Samba:")
+ logger.info(provision_backend.slapd_command_escaped)
+ logger.info("This slapd-Commandline is also stored under: %s/ldap_backend_startup.sh",
+ provision_backend.ldapdir)
result = ProvisionResult()
result.domaindn = domaindn
return result
-
def provision_become_dc(setup_dir=None,
smbconf=None, targetdir=None, realm=None,
rootdn=None, domaindn=None, schemadn=None,
ldap_backend=None, ldap_backend_type=None,
sitename=None, debuglevel=1):
- def message(text):
- """print a message if quiet is not set."""
- print text
-
- glue.set_debug_level(debuglevel)
+ logger = logging.getLogger("provision")
+ samba.set_debug_level(debuglevel)
- return provision(setup_dir, message, system_session(), None,
+ return provision(setup_dir, logger, system_session(), None,
smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
configdn=configdn, serverdn=serverdn, domain=domain,
{"S4_LDAPI_URI": ldapi_uri})
-def create_zone_file(lp, message, paths, targetdir, setup_path, dnsdomain,
+def create_zone_file(lp, logger, paths, targetdir, setup_path, dnsdomain,
hostip, hostip6, hostname, realm, domainguid,
ntdsguid):
"""Write out a DNS zone file, from the info in the current database.
if hostip6 is not None:
hostip6_base_line = " IN AAAA " + hostip6
hostip6_host_line = hostname + " IN AAAA " + hostip6
+ gc_msdcs_ip6_line = "gc._msdcs IN AAAA " + hostip6
else:
hostip6_base_line = ""
hostip6_host_line = ""
+ gc_msdcs_ip6_line = ""
if hostip is not None:
hostip_base_line = " IN A " + hostip
hostip_host_line = hostname + " IN A " + hostip
+ gc_msdcs_ip_line = "gc._msdcs IN A " + hostip
else:
hostip_base_line = ""
hostip_host_line = ""
+ gc_msdcs_ip_line = ""
dns_dir = os.path.dirname(paths.dns)
# we need to freeze the zone while we update the contents
if targetdir is None:
- rndc = lp.get("rndc command")
+ rndc = ' '.join(lp.get("rndc command"))
os.system(rndc + " freeze " + lp.get("realm"))
setup_file(setup_path("provision.zone"), paths.dns, {
"NTDSGUID": ntdsguid,
"HOSTIP6_BASE_LINE": hostip6_base_line,
"HOSTIP6_HOST_LINE": hostip6_host_line,
+ "GC_MSDCS_IP_LINE": gc_msdcs_ip_line,
+ "GC_MSDCS_IP6_LINE": gc_msdcs_ip6_line,
})
+ # note that we use no variable substitution on this file
+ # the substitution is done at runtime by samba_dnsupdate
+ setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
+
+ # and the SPN update list
+ setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
+
if paths.bind_gid is not None:
try:
os.chown(dns_dir, -1, paths.bind_gid)
os.chmod(dns_dir, 0775)
os.chmod(paths.dns, 0664)
except OSError:
- message("Failed to chown %s to bind gid %u" % (dns_dir, paths.bind_gid))
+ logger.error("Failed to chown %s to bind gid %u" % (dns_dir, paths.bind_gid))
if targetdir is None:
os.system(rndc + " unfreeze " + lp.get("realm"))
+def create_dns_update_list(lp, logger, paths, setup_path):
+ """Write out a dns_update_list file"""
+ # note that we use no variable substitution on this file
+ # the substitution is done at runtime by samba_dnsupdate
+ setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
+ setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
+
+
def create_named_conf(paths, setup_path, realm, dnsdomain,
private_dir):
"""Write out a file containing zone statements suitable for inclusion in a
setup_file(setup_path("named.conf.update"), paths.namedconf_update)
+
def create_named_txt(path, setup_path, realm, dnsdomain,
private_dir, keytab_name):
"""Write out a file containing zone statements suitable for inclusion in a
"PRIVATE_DIR": private_dir
})
+
def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
"""Write out a file containing zone statements suitable for inclusion in a
named.conf file (including GSS-TSIG configuration).
:param hostname: Local hostname
:param realm: Realm name
"""
-
setup_file(setup_path("krb5.conf"), path, {
"DNSDOMAIN": dnsdomain,
"HOSTNAME": hostname,
})
+class ProvisioningError(Exception):
+ """A generic provision error."""
+
+ def __init__(self, value):
+ self.value = value
+
+ def __str__(self):
+ return "ProvisioningError: " + self.value
+
+
+class InvalidNetbiosName(Exception):
+ """A specified name was not a valid NetBIOS name."""
+ def __init__(self, name):
+ super(InvalidNetbiosName, self).__init__("The name '%r' is not a valid NetBIOS name" % name)