-#
+
# 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
+from samba.dsdb import DS_DOMAIN_FUNCTION_2003, DS_DOMAIN_FUNCTION_2008_R2, ENC_ALL_TYPES
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"
+DEFAULT_POLICY_GUID = "31B2F340-016D-11D2-945F-00C04FB984F9"
+DEFAULT_DC_POLICY_GUID = "6AC1786C-016F-11D2-945F-00C04fB984F9"
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
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")
"""
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):
+def setup_modify_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]):
"""Modify a ldb in the private dir.
:param ldb: LDB object.
:param subst_vars: Optional dictionary with substitution variables.
"""
data = read_and_sub_file(ldif_path, subst_vars)
-
- ldb.modify_ldif(data)
+ ldb.modify_ldif(data, controls)
def setup_ldb(ldb, ldif_path, subst_vars):
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.keytab = "secrets.keytab"
paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
paths.samdb = os.path.join(paths.private_dir, lp.get("sam database") or "samdb.ldb")
paths.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.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
domain = domain.upper()
if 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))
+ raise ProvisioningError("guess_names: Workgroup '%s' in smb.conf must match chosen domain '%s'! Please remove the %s file and let provision generate it" % (lp.get("workgroup").upper(), domain, lp.configfile))
if domaindn is None:
domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
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 = ""
else:
sid_generator_line = "sid generator = " + sid_generator
+ used_setup_dir = setup_path("")
+ default_setup_dir = default_lp.get("setup directory")
+ setupdir_line = ""
+ if used_setup_dir != default_setup_dir:
+ setupdir_line = "setup directory = %s" % used_setup_dir
+ default_lp.set("setup directory", used_setup_dir)
+
sysvol = os.path.join(default_lp.get("lock dir"), "sysvol")
netlogon = os.path.join(sysvol, realm.lower(), "scripts")
"SERVERROLE": serverrole,
"NETLOGONPATH": netlogon,
"SYSVOLPATH": sysvol,
+ "SETUPDIRECTORY_LINE": setupdir_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.
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,
"priorChanged",
"krb5Keytab",
"privateKeytab"]
-
- 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"]
if realm is not None:
if dnsdomain is None:
- dnsdomain = realm.lower()
+ dnsdomain = realm.lower()
+ dnsname = '%s.%s' % (netbiosname.lower(), dnsdomain.lower())
+ else:
+ dnsname = None
+ shortname = netbiosname.lower()
+
+ #We don't need to set msg["flatname"] here, because rdn_name will handle it, and it causes problems for modifies anyway
+ msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
+ msg["secureChannelType"] = [str(secure_channel_type)]
+ msg["objectClass"] = ["top", "primaryDomain"]
+ if dnsname is not None:
msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
- msg["realm"] = realm
- msg["saltPrincipal"] = "host/%s.%s@%s" % (netbiosname.lower(), dnsdomain.lower(), realm.upper())
+ msg["realm"] = [realm]
+ msg["saltPrincipal"] = ["host/%s@%s" % (dnsname, realm.upper())]
msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
msg["privateKeytab"] = ["secrets.keytab"]
-
msg["secret"] = [machinepass]
msg["samAccountName"] = ["%s$" % netbiosname]
msg["secureChannelType"] = [str(secure_channel_type)]
if domainsid is not None:
msg["objectSid"] = [ndr_pack(domainsid)]
+ # This complex expression tries to ensure that we don't have more
+ # than one record for this SID, realm or netbios domain at a time,
+ # 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))" % (domain, realm, str(domainsid))),
+ expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(dn=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
scope=ldb.SCOPE_ONELEVEL)
for del_msg in res:
- if del_msg.dn is not msg.dn:
secretsdb.delete(del_msg.dn)
res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
if len(res) == 1:
- msg["priorSecret"] = res[0]["secret"]
- msg["priorWhenChanged"] = res[0]["whenChanged"]
+ msg["priorSecret"] = [res[0]["secret"][0]]
+ msg["priorWhenChanged"] = [res[0]["whenChanged"][0]]
- if res["privateKeytab"] is not None:
- msg["privateKeytab"] = res[0]["privateKeytab"]
+ try:
+ msg["privateKeytab"] = [res[0]["privateKeytab"][0]]
+ except KeyError:
+ pass
- if res["krb5Keytab"] is not None:
- msg["krb5Keytab"] = res[0]["krb5Keytab"]
+ try:
+ msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]]
+ except KeyError:
+ pass
for el in msg:
- el.set_flags(ldb.FLAG_MOD_REPLACE)
- secretsdb.modify(msg)
+ if el != 'dn':
+ msg[el].set_flags(ldb.FLAG_MOD_REPLACE)
+ secretsdb.modify(msg)
+ secretsdb.rename(res[0].dn, msg.dn)
else:
+ 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
+ spn.extend([ 'HOST/%s' % dnsname ])
+ msg["servicePrincipalName"] = spn
+
secretsdb.add(msg)
-def secretsdb_setup_dns(secretsdb, setup_path, private_dir,
+def secretsdb_setup_dns(secretsdb, setup_path, names, private_dir,
realm, dnsdomain,
dns_keytab_path, dnspass):
"""Add DNS specific bits to a secrets database.
"DNSDOMAIN": dnsdomain,
"DNS_KEYTAB": dns_keytab_path,
"DNSPASS_B64": b64encode(dnspass),
+ "HOSTNAME": names.hostname,
+ "DNSNAME" : '%s.%s' % (names.netbiosname.lower(), names.dnsdomain.lower())
})
-def setup_secretsdb(path, setup_path, session_info, backend_credentials, lp):
+def setup_secretsdb(paths, 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.
:param lp: Loadparm context
:return: LDB handle for the created secrets database
"""
- if os.path.exists(path):
- os.unlink(path)
+ if os.path.exists(paths.secrets):
+ os.unlink(paths.secrets)
+
+ keytab_path = os.path.join(paths.private_dir, paths.keytab)
+ if os.path.exists(keytab_path):
+ os.unlink(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)
+
+ path = paths.secrets
+
secrets_ldb = Ldb(path, session_info=session_info,
lp=lp)
secrets_ldb.erase()
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)
"""
setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
"SCHEMADN": names.schemadn,
- "NETBIOSNAME": names.netbiosname,
- "DNSDOMAIN": names.dnsdomain,
- "REALM": names.realm,
- "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
"DOMAINDN": names.domaindn,
"ROOTDN": names.rootdn,
"CONFIGDN": names.configdn,
def setup_self_join(samdb, names,
machinepass, dnspass,
- domainsid, invocationid, setup_path,
+ domainsid, next_rid, invocationid, setup_path,
policyguid, policyguid_dc, domainControllerFunctionality,
ntdsguid):
"""Join a host to its own domain."""
"REALM": names.realm,
"DOMAIN": names.domain,
"DOMAINSID": str(domainsid),
+ "DCRID": str(next_rid),
"DNSDOMAIN": names.dnsdomain,
"SAMBA_VERSION_STRING": version,
"NTDSGUID": ntdsguid_line,
"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,%s" % names.serverdn
names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID",
expression="", scope=ldb.SCOPE_BASE)
assert isinstance(names.ntdsguid, str)
"SERVERDN": names.serverdn,
"NETBIOSNAME": names.netbiosname,
"NTDSGUID": names.ntdsguid,
+ "RIDALLOCATIONSTART": str(next_rid + 100),
+ "RIDALLOCATIONEND": str(next_rid + 100 + 499),
+ })
+
+ # This is partially Samba4 specific and should be replaced by the correct
+ # DNS AD-style setup
+ setup_add_ldif(samdb, setup_path("provision_dns_add.ldif"), {
+ "DNSDOMAIN": names.dnsdomain,
+ "DOMAINDN": names.domaindn,
"DNSPASS_B64": b64encode(dnspass),
+ "HOSTNAME" : names.hostname,
+ "DNSNAME" : '%s.%s' % (names.netbiosname.lower(), names.dnsdomain.lower())
})
+def getpolicypath(sysvolpath, dnsdomain, guid):
+ """Return the physical path of policy given its guid.
+
+ :param sysvolpath: Path to the sysvol folder
+ :param dnsdomain: DNS name of the AD domain
+ :param guid: The GUID of the policy
+ :return: A string with the complete path to the policy folder
+ """
+
+ 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 + "}")
- os.makedirs(policy_path, 0755)
+def create_gpo_struct(policy_path):
+ if not os.path.exists(policy_path):
+ os.makedirs(policy_path, 0775)
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):
+ "[General]\r\nVersion=0")
+ p = os.path.join(policy_path, "MACHINE")
+ if not os.path.exists(p):
+ os.makedirs(p, 0775)
+ p = os.path.join(policy_path, "USER")
+ if not os.path.exists(p):
+ os.makedirs(p, 0775)
+
+
+def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
+ """Create the default GPO for a domain
+
+ :param sysvolpath: Physical path for the sysvol folder
+ :param dnsdomain: DNS domain name of the AD domain
+ :param policyguid: GUID of the default domain policy
+ :param policyguid_dc: GUID of the default domain controler policy
+ """
+
+ 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,
+ next_rid=1000):
"""Setup a complete SAM Database.
:note: This will wipe the main SAM database file!
"""
+
+ # Provision does not make much sense values larger than 1000000000
+ # as the upper range of the rIDAvailablePool is 1073741823 and
+ # we don't want to create a domain that cannot allocate rids.
+ if next_rid < 1000 or next_rid > 1000000000:
+ error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
+ error += "the valid range is %u-%u. The default is %u." % (1000, 1000000000, 1000)
+ raise ProvisioningError(error)
+
# ATTENTION: Do NOT change these default values without discussion with the
# team and/or release manager. They have a big impact on the whole program!
- domainControllerFunctionality = DS_DC_FUNCTION_2008
+ domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
if dom_for_fun_level is None:
dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
- if dom_for_fun_level < 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!")
+ raise ProvisioningError("You want to run SAMBA 4 on a domain and forest function level which itself is higher than its actual DC function level (2008_R2). This won't work!")
domainFunctionality = dom_for_fun_level
forestFunctionality = dom_for_fun_level
# Also wipes the database
- setup_samdb_partitions(path, setup_path, 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):
+ if schema is None:
schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn)
- # 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)
+
+ # Set the NTDS settings DN manually - in order to have it already around
+ # before the provisioned tree exists and we connect
+ samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
# 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)
- message("Adding DomainDN: %s" % names.domaindn)
+ logger.info("Adding DomainDN: %s" % names.domaindn)
#impersonate domain admin
admin_session_info = admin_session(lp, str(domainsid))
setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
"CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
"DOMAINSID": str(domainsid),
+ "NEXTRID": str(next_rid),
"SCHEMADN": names.schemadn,
"NETBIOSNAME": names.netbiosname,
"DEFAULTSITE": names.sitename,
"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, auto_connect=False,
+ credentials=provision_backend.credentials, lp=lp,
+ global_schema=False, am_rodc=am_rodc)
+
+ # Set the NTDS settings DN manually - in order to have it already around
+ # before the provisioned tree exists and we connect
+ samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
+
+ samdb.connect(path)
+
+ samdb.transaction_start()
+ try:
+ samdb.invocation_id = invocationid
+
+ logger.info("Setting up sam.ldb configuration data")
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,
"DEFAULTSITE": names.sitename,
"CONFIGDN": names.configdn,
"SERVERDN": names.serverdn,
+ "RIDAVAILABLESTART": str(next_rid + 600),
"POLICYGUID_DC": policyguid_dc
})
"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,
- domainsid=domainsid, policyguid=policyguid,
+ domainsid=domainsid,
+ next_rid=next_rid,
+ policyguid=policyguid,
policyguid_dc=policyguid_dc,
setup_path=setup_path,
domainControllerFunctionality=domainControllerFunctionality,
ntdsguid=ntdsguid)
- ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
+ 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)
-
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_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp):
+ """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
+ folders beneath.
+
+ :param sysvol: Physical path for the sysvol folder
+ :param dnsdomain: The DNS name of the domain
+ :param domainsid: The SID of the domain
+ :param domaindn: The DN of the domain (ie. DC=...)
+ :param samdb: An LDB object on the SAM db
+ :param lp: an LP object
+ """
+
+ # Set ACL for GPO root folder
+ root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
+ setntacl(lp, root_policy_path, POLICIES_ACL, 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):
+ """Set the ACL for the sysvol share and the subfolders
+
+ :param samdb: An LDB object on the SAM db
+ :param netlogon: Physical path for the netlogon folder
+ :param sysvol: Physical path for the sysvol folder
+ :param gid: The GID of the "Domain adminstrators" group
+ :param domainsid: The SID of the domain
+ :param dnsdomain: The DNS name of the domain
+ :param domaindn: The DN of the domain (ie. DC=...)
+ """
+
+ try:
+ os.chown(sysvol,-1,gid)
+ except:
+ canchown = False
+ else:
+ canchown = True
+
+ # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
+ 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 acls on Policy folder and policies folders
+ set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp)
+
+
+def provision(setup_dir, logger, session_info,
credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL,
realm=None,
rootdn=None, domaindn=None, schemadn=None, configdn=None,
serverdn=None,
domain=None, hostname=None, hostip=None, hostip6=None,
- domainsid=None, adminpass=None, ldapadminpass=None,
+ domainsid=None, next_rid=1000,
+ adminpass=None, ldapadminpass=None,
krbtgtpass=None, domainguid=None,
policyguid=None, policyguid_dc=None, invocationid=None,
machinepass=None, ntdsguid=None,
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!
domainsid = security.dom_sid(domainsid)
# create/adapt the group policy GUIDs
+ # Default GUID for default policy are described at
+ # "How Core Group Policy Works"
+ # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
if policyguid is None:
- policyguid = str(uuid.uuid4())
+ policyguid = DEFAULT_POLICY_GUID
policyguid = policyguid.upper()
if policyguid_dc is None:
- policyguid_dc = str(uuid.uuid4())
+ policyguid_dc = DEFAULT_DC_POLICY_GUID
policyguid_dc = policyguid_dc.upper()
if adminpass is None:
- adminpass = glue.generate_random_password(12, 32)
+ adminpass = samba.generate_random_password(12, 32)
if krbtgtpass is None:
- krbtgtpass = glue.generate_random_password(128, 255)
+ krbtgtpass = samba.generate_random_password(128, 255)
if machinepass is None:
- machinepass = glue.generate_random_password(128, 255)
+ machinepass = samba.generate_random_password(128, 255)
if dnspass is None:
- dnspass = glue.generate_random_password(128, 255)
+ 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_password(128, 255)
+ ldapadminpass=samba.generate_random_password(128, 255)
if backend_type is None:
backend_type = "ldb"
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:
- hostips = glue.interface_ips(lp, False)
+ hostips = samba.interface_ips(lp, False)
if len(hostips) == 0:
- message("No external IPv4 address has been found: I use the loopback.")
+ logger.warning("No external IPv4 address has been found. Using loopback.")
hostip = '127.0.0.1'
else:
hostip = hostips[0]
if len(hostips) > 1:
- message("More than one IPv4 address found: I use " + hostip + ".")
+ logger.warning("More than one IPv4 address found. Using %s.", hostip)
if hostip6 is None:
try:
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)
+
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")
- 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)
+ logger.info("Setting up secrets.ldb")
+ secrets_ldb = setup_secretsdb(paths, setup_path,
+ session_info=session_info,
+ backend_credentials=provision_backend.secrets_credentials, lp=lp)
- 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)
-
- 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, next_rid=next_rid)
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)
+ 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)
+ create_default_gpo(paths.sysvol, names.dnsdomain, policyguid, policyguid_dc)
+ setsysvolacl(samdb, paths.netlogon, paths.sysvol, wheel_gid,
+ domainsid, names.dnsdomain, names.domaindn, lp)
+
+ logger.info("Setting up sam.ldb rootDSE marking as synchronized")
+ setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
+
+ secretsdb_self_join(secrets_ldb, domain=names.domain,
+ realm=names.realm,
+ dnsdomain=names.dnsdomain,
+ netbiosname=names.netbiosname,
+ domainsid=domainsid,
+ machinepass=machinepass,
+ secure_channel_type=SEC_CHAN_BDC)
+
+ # Now set up the right msDS-SupportedEncryptionTypes into the DB
+ # In future, this might be determined from some configuration
+ kerberos_enctypes = str(ENC_ALL_TYPES)
+
+ try:
+ msg = ldb.Message(ldb.Dn(samdb, samdb.searchone("distinguishedName", expression="samAccountName=%s$" % names.netbiosname, scope=ldb.SCOPE_SUBTREE)))
+ msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(elements=kerberos_enctypes,
+ flags=ldb.FLAG_MOD_REPLACE,
+ name="msDS-SupportedEncryptionTypes")
+ samdb.modify(msg)
+ except ldb.LdbError, (ldb.ERR_NO_SUCH_ATTRIBUTE, _):
+ # It might be that this attribute does not exist in this schema
+ pass
+
+
+ if serverrole == "domain controller":
+ secretsdb_setup_dns(secrets_ldb, setup_path, names,
+ paths.private_dir,
+ realm=names.realm, dnsdomain=names.dnsdomain,
+ dns_keytab_path=paths.dns_keytab,
+ dnspass=dnspass)
+
+ domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
+ assert isinstance(domainguid, str)
+
+ # Only make a zone file on the first DC, it should be replicated
+ # with DNS replication
+ create_zone_file(lp, logger, paths, targetdir, setup_path,
+ dnsdomain=names.dnsdomain, hostip=hostip, hostip6=hostip6,
+ hostname=names.hostname, realm=names.realm,
+ domainguid=domainguid, ntdsguid=names.ntdsguid)
+
+ create_named_conf(paths, setup_path, realm=names.realm,
+ dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
+
+ create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
+ dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
+ keytab_name=paths.dns_keytab)
+ logger.info("See %s for an example configuration include file for BIND", paths.namedconf)
+ logger.info("and %s for further documentation required for secure DNS "
+ "updates", paths.namedtxt)
+
+ lastProvisionUSNs = get_last_provision_usn(samdb)
+ maxUSN = get_max_usn(samdb, str(names.rootdn))
+ if lastProvisionUSNs is not None:
+ update_provision_usn(samdb, 0, maxUSN, 1)
+ else:
+ set_provision_usn(samdb, 0, maxUSN)
+
+ create_krb5_conf(paths.krb5conf, setup_path,
+ dnsdomain=names.dnsdomain, hostname=names.hostname,
+ realm=names.realm)
+ logger.info("A Kerberos configuration suitable for Samba 4 has been "
+ "generated at %s", paths.krb5conf)
- if serverrole == "domain controller":
- create_dns_update_list(lp, message, paths, setup_path)
+ 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)
+ 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))
+ if not os.environ.has_key('SAMBA_SELFTEST'):
+ 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: " + provision_backend.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
+ logger = logging.getLogger("provision")
+ samba.set_debug_level(debuglevel)
- glue.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)
"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))
+ if not os.environ.has_key('SAMBA_SELFTEST'):
+ 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, message, paths, setup_path):
+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,
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)