X-Git-Url: http://git.samba.org/samba.git/?p=ira%2Fwip.git;a=blobdiff_plain;f=source4%2Fscripting%2Fpython%2Fsamba%2Fprovision.py;h=fb4e9b71f57d93f2d6e33ba06e84b4ad15ab5667;hp=fdf1fe9e61d0e316134c03ad2c773d7fb5aa3256;hb=5a72eca574f2cb330a0d62936ca901b8ebee4194;hpb=aaca10b3e13d5a6119d7f47bc21bbf0cac3efb96 diff --git a/source4/scripting/python/samba/provision.py b/source4/scripting/python/samba/provision.py index fdf1fe9e61d..fb4e9b71f57 100644 --- a/source4/scripting/python/samba/provision.py +++ b/source4/scripting/python/samba/provision.py @@ -27,34 +27,31 @@ from base64 import b64encode import os -import sys import pwd import grp import time -import uuid, glue +import uuid import socket import param import registry -import samba -import subprocess +import urllib +import shutil + import ldb -import shutil -from credentials import Credentials, DONT_USE_KERBEROS -from auth import system_session, admin_session -from samba import version, Ldb, substitute_var, valid_netbios_name -from samba import check_all_substituted -from samba import DS_DOMAIN_FUNCTION_2003, DS_DOMAIN_FUNCTION_2008, DS_DC_FUNCTION_2008 -from samba.samdb import SamDB -from samba.idmap import IDmapDB +from samba.auth import system_session, admin_session +from samba import glue, 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.dcerpc import security -from samba.ndr import ndr_pack -import urllib -from ldb import SCOPE_SUBTREE, SCOPE_ONELEVEL, SCOPE_BASE, LdbError, timestring -from ms_schema import read_ms_schema +from samba.dcerpc.misc import SEC_CHAN_BDC, SEC_CHAN_WKSTA +from samba.idmap import IDmapDB +from samba.ntacls import setntacl, dsacl2fsacl +from samba.ndr import ndr_pack,ndr_unpack +from samba.schema import Schema from ms_display_specifiers import read_ms_ldif -from signal import SIGTERM -from dcerpc.misc import SEC_CHAN_BDC, SEC_CHAN_WKSTA +from samba.provisionbackend import LDBBackend, ExistingBackend, FDSBackend, OpenLDAPBackend +from provisionexceptions import ProvisioningError, InvalidNetbiosName __docformat__ = "restructuredText" @@ -73,19 +70,9 @@ def find_setup_dir(): return ret raise Exception("Unable to find setup directory.") -def get_schema_descriptor(domain_sid): - sddl = "O:SAG:SAD:(A;CI;RPLCLORC;;;AU)(A;CI;RPWPCRCCLCLORCWOWDSW;;;SA)" \ - "(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \ - "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \ - "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \ - "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \ - "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \ - "S:(AU;SA;WPCCDCWOWDSDDTSW;;;WD)" \ - "(AU;CISA;WP;;;WD)(AU;SA;CR;;;BA)" \ - "(AU;SA;CR;;;DU)(OU;SA;CR;e12b56b6-0a95-11d1-adbb-00c04fd8d5cd;;WD)" \ - "(OU;SA;CR;45ec5156-db7e-47bb-b53f-dbeb2d03c40f;;WD)" - sec = security.descriptor.from_sddl(sddl, domain_sid) - return b64encode(ndr_pack(sec)) +# descriptors of the naming contexts +# 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)" \ @@ -100,26 +87,69 @@ def get_config_descriptor(domain_sid): "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \ "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \ "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \ - "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;S-1-5-21-3191434175-1265308384-3577286990-498)" \ + "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \ "S:(AU;SA;WPWOWD;;;WD)(AU;SA;CR;;;BA)(AU;SA;CR;;;DU)" \ "(OU;SA;CR;45ec5156-db7e-47bb-b53f-dbeb2d03c40f;;WD)" sec = security.descriptor.from_sddl(sddl, domain_sid) - return b64encode(ndr_pack(sec)) - + return ndr_pack(sec) + +def get_domain_descriptor(domain_sid): + sddl= "O:BAG:BAD:AI(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \ + "(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \ + "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \ + "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \ + "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \ + "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \ + "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \ + "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \ + "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \ + "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \ + "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \ + "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;DD)" \ + "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a86-0de6-11d0-a285-00aa003049e2;ED)" \ + "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a9c-0de6-11d0-a285-00aa003049e2;ED)" \ + "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967aba-0de6-11d0-a285-00aa003049e2;ED)" \ + "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \ + "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \ + "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \ + "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \ + "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \ + "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \ + "(OA;;CR;e2a36dc9-ae17-47c3-b58b-be34c55ba633;;IF)" \ + "(OA;;RP;c7407360-20bf-11d0-a768-00aa006e0529;;RU)" \ + "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;RU)" \ + "(OA;CIIO;RPLCLORC;;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \ + "(OA;CIIO;RPLCLORC;;bf967a9c-0de6-11d0-a285-00aa003049e2;RU)" \ + "(OA;CIIO;RPLCLORC;;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \ + "(OA;;CR;05c74c5e-4deb-43b4-bd9f-86664c2a7fd5;;AU)" \ + "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \ + "(OA;;CR;ccc2dc7d-a6ad-4a7a-8846-c04e3cc53501;;AU)" \ + "(OA;;CR;280f369c-67c7-438e-ae98-1d46f3c6f541;;AU)" \ + "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \ + "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \ + "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \ + "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \ + "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;AU)" \ + "(OA;CIIO;RPWPCR;91e647de-d96f-4b70-9557-d63ff4f3ccd8;;PS)" \ + "(A;;RPWPCRCCLCLORCWOWDSW;;;DA)" \ + "(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \ + "(A;;RPRC;;;RU)" \ + "(A;CI;LC;;;RU)" \ + "(A;CI;RPWPCRCCLCLORCWOWDSDSW;;;BA)" \ + "(A;;RP;;;WD)" \ + "(A;;RPLCLORC;;;ED)" \ + "(A;;RPLCLORC;;;AU)" \ + "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \ + "S:AI(OU;CISA;WP;f30e3bbe-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \ + "(OU;CISA;WP;f30e3bbf-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \ + "(AU;SA;CR;;;DU)(AU;SA;CR;;;BA)(AU;SA;WPWOWD;;;WD)" + sec = security.descriptor.from_sddl(sddl, domain_sid) + return ndr_pack(sec) DEFAULTSITE = "Default-First-Site-Name" # Exception classes -class ProvisioningError(Exception): - """A generic provision error.""" - -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) - - class ProvisionPaths(object): def __init__(self): self.shareconf = None @@ -141,14 +171,6 @@ class ProvisionPaths(object): self.slapdconf = None self.modulesconf = None self.memberofconf = None - self.fedoradsinf = None - self.fedoradspartitions = None - self.fedoradssasl = None - self.fedoradspam = None - self.fedoradsrefint = None - self.fedoradslinkedattributes = None - self.fedoradsindex = None - self.fedoradssamba = None self.olmmron = None self.olmmrserveridsconf = None self.olmmrsyncreplconf = None @@ -163,7 +185,6 @@ class ProvisionNames(object): self.domaindn = None self.configdn = None self.schemadn = None - self.sambadn = None self.ldapmanagerdn = None self.dnsdomain = None self.realm = None @@ -180,74 +201,7 @@ class ProvisionResult(object): self.domaindn = None self.lp = None self.samdb = None - -class Schema(object): - def __init__(self, setup_path, domain_sid, schemadn=None, - serverdn=None, sambadn=None, ldap_backend_type=None): - """Load schema for the SamDB from the AD schema files and samba4_schema.ldif - - :param samdb: Load a schema into a SamDB. - :param setup_path: Setup path function. - :param schemadn: DN of the schema - :param serverdn: DN of the server - - Returns the schema data loaded, to avoid double-parsing when then needing to add it to the db - """ - - self.ldb = Ldb() - self.schema_data = read_ms_schema(setup_path('ad-schema/MS-AD_Schema_2K8_Attributes.txt'), - setup_path('ad-schema/MS-AD_Schema_2K8_Classes.txt')) - self.schema_data += open(setup_path("schema_samba4.ldif"), 'r').read() - self.schema_data = substitute_var(self.schema_data, {"SCHEMADN": schemadn}) - check_all_substituted(self.schema_data) - - self.schema_dn_modify = read_and_sub_file(setup_path("provision_schema_basedn_modify.ldif"), - {"SCHEMADN": schemadn, - "SERVERDN": serverdn, - }) - - descr = get_schema_descriptor(domain_sid) - self.schema_dn_add = read_and_sub_file(setup_path("provision_schema_basedn.ldif"), - {"SCHEMADN": schemadn, - "DESCRIPTOR": descr - }) - - prefixmap = open(setup_path("prefixMap.txt"), 'r').read() - prefixmap = b64encode(prefixmap) - - - # We don't actually add this ldif, just parse it - prefixmap_ldif = "dn: cn=schema\nprefixMap:: %s\n\n" % prefixmap - self.ldb.set_schema_from_ldif(prefixmap_ldif, self.schema_data) - - -# Return a hash with the forward attribute as a key and the back as the value -def get_linked_attributes(schemadn,schemaldb): - attrs = ["linkID", "lDAPDisplayName"] - res = schemaldb.search(expression="(&(linkID=*)(!(linkID:1.2.840.113556.1.4.803:=1))(objectclass=attributeSchema)(attributeSyntax=2.5.5.1))", base=schemadn, scope=SCOPE_ONELEVEL, attrs=attrs) - attributes = {} - for i in range (0, len(res)): - expression = "(&(objectclass=attributeSchema)(linkID=%d)(attributeSyntax=2.5.5.1))" % (int(res[i]["linkID"][0])+1) - target = schemaldb.searchone(basedn=schemadn, - expression=expression, - attribute="lDAPDisplayName", - scope=SCOPE_SUBTREE) - if target is not None: - attributes[str(res[i]["lDAPDisplayName"])]=str(target) - - return attributes - -def get_dnsyntax_attributes(schemadn,schemaldb): - attrs = ["linkID", "lDAPDisplayName"] - res = schemaldb.search(expression="(&(!(linkID=*))(objectclass=attributeSchema)(attributeSyntax=2.5.5.1))", base=schemadn, scope=SCOPE_ONELEVEL, attrs=attrs) - attributes = [] - for i in range (0, len(res)): - attributes.append(str(res[i]["lDAPDisplayName"])) - - return attributes - - def check_install(lp, session_info, credentials): """Check whether the current install seems ok. @@ -282,19 +236,6 @@ findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2] findnss_gid = lambda names: findnss(grp.getgrnam, names)[2] -def read_and_sub_file(file, subst_vars): - """Read a file and sub in variables found in it - - :param file: File to be read (typically from setup directory) - param subst_vars: Optional variables to subsitute in the file. - """ - data = open(file, 'r').read() - if subst_vars is not None: - data = substitute_var(data, subst_vars) - check_all_substituted(data) - return data - - def setup_add_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]): """Setup a ldb in the private dir. @@ -339,22 +280,6 @@ def setup_ldb(ldb, ldif_path, subst_vars): ldb.transaction_commit() -def setup_file(template, fname, subst_vars=None): - """Setup a file in the private dir. - - :param template: Path of the template file. - :param fname: Path of the file to create. - :param subst_vars: Substitution variables. - """ - f = fname - - if os.path.exists(f): - os.unlink(f) - - data = read_and_sub_file(template, subst_vars) - open(f, 'w').write(data) - - def provision_paths_from_lp(lp, dnsdomain): """Set the default paths for provisioning. @@ -369,7 +294,8 @@ def provision_paths_from_lp(lp, dnsdomain): paths.samdb = os.path.join(paths.private_dir, lp.get("sam database") or "samdb.ldb") paths.idmapdb = os.path.join(paths.private_dir, lp.get("idmap database") or "idmap.ldb") paths.secrets = os.path.join(paths.private_dir, lp.get("secrets database") or "secrets.ldb") - paths.dns = os.path.join(paths.private_dir, dnsdomain + ".zone") + paths.privilege = os.path.join(paths.private_dir, "privilege.ldb") + paths.dns = os.path.join(paths.private_dir, "dns", dnsdomain + ".zone") paths.namedconf = os.path.join(paths.private_dir, "named.conf") paths.namedtxt = os.path.join(paths.private_dir, "named.txt") paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf") @@ -387,22 +313,6 @@ def provision_paths_from_lp(lp, dnsdomain): "modules.conf") paths.memberofconf = os.path.join(paths.ldapdir, "memberof.conf") - paths.fedoradsinf = os.path.join(paths.ldapdir, - "fedorads.inf") - paths.fedoradspartitions = os.path.join(paths.ldapdir, - "fedorads-partitions.ldif") - paths.fedoradssasl = os.path.join(paths.ldapdir, - "fedorads-sasl.ldif") - paths.fedoradspam = os.path.join(paths.ldapdir, - "fedorads-pam.ldif") - paths.fedoradsrefint = os.path.join(paths.ldapdir, - "fedorads-refint.ldif") - paths.fedoradslinkedattributes = os.path.join(paths.ldapdir, - "fedorads-linked-attributes.ldif") - paths.fedoradsindex = os.path.join(paths.ldapdir, - "fedorads-index.ldif") - paths.fedoradssamba = os.path.join(paths.ldapdir, - "fedorads-samba.ldif") paths.olmmrserveridsconf = os.path.join(paths.ldapdir, "mmr_serverids.conf") paths.olmmrsyncreplconf = os.path.join(paths.ldapdir, @@ -429,58 +339,68 @@ def provision_paths_from_lp(lp, dnsdomain): def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None, serverrole=None, rootdn=None, domaindn=None, configdn=None, - schemadn=None, serverdn=None, sitename=None, sambadn=None): + schemadn=None, serverdn=None, sitename=None): """Guess configuration settings to use.""" if hostname is None: - hostname = socket.gethostname().split(".")[0].lower() + hostname = socket.gethostname().split(".")[0] - netbiosname = hostname.upper() + netbiosname = lp.get("netbios name") + if netbiosname is None: + netbiosname = hostname + assert netbiosname is not None + netbiosname = netbiosname.upper() if not valid_netbios_name(netbiosname): raise InvalidNetbiosName(netbiosname) - hostname = hostname.lower() - if dnsdomain is None: - dnsdomain = lp.get("realm").lower() + dnsdomain = lp.get("realm") + if dnsdomain is None or dnsdomain == "": + raise ProvisioningError("guess_names: 'realm' not specified in supplied smb.conf!") + + 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!") + + serverrole = serverrole.lower() - assert dnsdomain is not None realm = dnsdomain.upper() if lp.get("realm").upper() != realm: - raise Exception("realm '%s' in %s must match chosen realm '%s'" % - (lp.get("realm"), lp.configfile, realm)) - + raise ProvisioningError("guess_names: Realm '%s' in smb.conf must match chosen realm '%s'!", lp.get("realm").upper(), realm) + + 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) + if serverrole == "domain controller": if domain is None: 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) + if domaindn is None: domaindn = "DC=" + dnsdomain.replace(".", ",DC=") - if lp.get("workgroup").upper() != domain.upper(): - raise Exception("workgroup '%s' in smb.conf must match chosen domain '%s'", - lp.get("workgroup"), domain) else: domain = netbiosname if domaindn is None: domaindn = "DC=" + netbiosname - assert domain is not None - domain = domain.upper() - if not valid_netbios_name(domain): raise InvalidNetbiosName(domain) - if netbiosname.upper() == realm: - raise Exception("realm %s must not be equal to netbios domain name %s", realm, netbiosname) - if hostname.upper() == realm: - raise Exception("realm %s must not be equal to hostname %s", realm, hostname) - - if domain.upper() == realm: - raise Exception("realm %s must not be equal to domain name %s", realm, domain) + 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) + if domain == realm: + raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!", realm, domain) if rootdn is None: rootdn = domaindn @@ -489,8 +409,6 @@ def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None, configdn = "CN=Configuration," + rootdn if schemadn is None: schemadn = "CN=Schema," + configdn - if sambadn is None: - sambadn = "CN=Samba" if sitename is None: sitename=DEFAULTSITE @@ -500,7 +418,6 @@ def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None, names.domaindn = domaindn names.configdn = configdn names.schemadn = schemadn - names.sambadn = sambadn names.ldapmanagerdn = "CN=Manager," + rootdn names.dnsdomain = dnsdomain names.domain = domain @@ -514,12 +431,13 @@ def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None, def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole, - targetdir): + targetdir, sid_generator,eadb): """Create a new smb.conf file based on a couple of basic settings. """ assert smbconf is not None if hostname is None: - hostname = socket.gethostname().split(".")[0].lower() + hostname = socket.gethostname().split(".")[0] + netbiosname = hostname.upper() if serverrole is None: serverrole = "standalone" @@ -532,14 +450,24 @@ def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole, elif serverrole == "standalone": smbconfsuffix = "standalone" + if sid_generator is None: + sid_generator = "internal" + assert domain is not None + domain = domain.upper() + assert realm is not None + realm = realm.upper() default_lp = param.LoadParm() #Load non-existant file if os.path.exists(smbconf): default_lp.load(smbconf) - + if eadb: + posixeadb_line = "posix:eadb = " + os.path.abspath(os.path.join(os.path.join(targetdir, "private"),"eadb.tdb")) + else: + posixeadb_line = "" + if targetdir is not None: privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private")) lockdir_line = "lock dir = " + os.path.abspath(targetdir) @@ -549,19 +477,26 @@ def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole, privatedir_line = "" lockdir_line = "" + if sid_generator == "internal": + sid_generator_line = "" + else: + sid_generator_line = "sid generator = " + sid_generator + sysvol = os.path.join(default_lp.get("lock dir"), "sysvol") netlogon = os.path.join(sysvol, realm.lower(), "scripts") setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix), smbconf, { - "HOSTNAME": hostname, + "NETBIOS_NAME": netbiosname, "DOMAIN": domain, "REALM": realm, "SERVERROLE": serverrole, "NETLOGONPATH": netlogon, "SYSVOLPATH": sysvol, + "SIDGENERATOR_LINE": sid_generator_line, "PRIVATEDIR_LINE": privatedir_line, - "LOCKDIR_LINE": lockdir_line + "LOCKDIR_LINE": lockdir_line, + "POSIXEADB_LINE": posixeadb_line }) @@ -585,8 +520,8 @@ def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid, idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid) def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info, - credentials, names, - serverrole, ldap_backend=None, + provision_backend, names, schema, + serverrole, erase=False): """Setup the partitions for the SAM database. @@ -597,111 +532,43 @@ def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info, :note: This function always removes the local SAM LDB file. The erase parameter controls whether to erase the existing data, which may not be stored locally but in LDAP. + """ 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 try: - samdb = Ldb(url=samdb_path, session_info=session_info, - credentials=credentials, lp=lp, options=["modules:"]) - # Wipes the database - samdb.erase_except_schema_controlled() - except LdbError: os.unlink(samdb_path) - samdb = Ldb(url=samdb_path, session_info=session_info, - credentials=credentials, lp=lp, options=["modules:"]) - # Wipes the database - samdb.erase_except_schema_controlled() - + except OSError: + pass - #Add modules to the list to activate them by default - #beware often order is important - # - # Some Known ordering constraints: - # - rootdse must be first, as it makes redirects from "" -> cn=rootdse - # - objectclass must be before password_hash, because password_hash checks - # that the objectclass is of type person (filled in by objectclass - # module when expanding the objectclass list) - # - partition must be last - # - each partition has its own module list then - modules_list = ["resolve_oids", - "rootdse", - "lazy_commit", - "acl", - "paged_results", - "ranged_results", - "anr", - "server_sort", - "asq", - "extended_dn_store", - "extended_dn_in", - "rdn_name", - "objectclass", - "descriptor", - "samldb", - "password_hash", - "operational", - "kludge_acl", - "instancetype"] - tdb_modules_list = [ - "subtree_rename", - "subtree_delete", - "linked_attributes", - "extended_dn_out_ldb"] - modules_list2 = ["show_deleted", - "partition"] - - domaindn_ldb = "users.ldb" - configdn_ldb = "configuration.ldb" - schemadn_ldb = "schema.ldb" - if ldap_backend is not None: - domaindn_ldb = ldap_backend.ldapi_uri - configdn_ldb = ldap_backend.ldapi_uri - schemadn_ldb = ldap_backend.ldapi_uri - - if ldap_backend.ldap_backend_type == "fedora-ds": - backend_modules = ["nsuniqueid", "paged_searches"] - # We can handle linked attributes here, as we don't have directory-side subtree operations - tdb_modules_list = ["extended_dn_out_dereference"] - elif ldap_backend.ldap_backend_type == "openldap": - backend_modules = ["entryuuid", "paged_searches"] - # OpenLDAP handles subtree renames, so we don't want to do any of these things - tdb_modules_list = ["extended_dn_out_dereference"] - - elif serverrole == "domain controller": - tdb_modules_list.insert(0, "repl_meta_data") - backend_modules = [] - else: - backend_modules = ["objectguid"] + samdb = Ldb(url=samdb_path, session_info=session_info, + lp=lp, options=["modules:"]) + + ldap_backend_line = "# No LDAP backend" + if provision_backend.type is not "ldb": + ldap_backend_line = "ldapBackend: %s" % provision_backend.ldapi_uri - if tdb_modules_list is None: - tdb_modules_list_as_string = "" - else: - tdb_modules_list_as_string = ","+",".join(tdb_modules_list) - samdb.transaction_start() try: message("Setting up sam.ldb partitions and settings") setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), { - "SCHEMADN": names.schemadn, - "SCHEMADN_LDB": schemadn_ldb, - "SCHEMADN_MOD2": ",objectguid", - "CONFIGDN": names.configdn, - "CONFIGDN_LDB": configdn_ldb, - "DOMAINDN": names.domaindn, - "DOMAINDN_LDB": domaindn_ldb, - "SCHEMADN_MOD": "schema_fsmo", - "CONFIGDN_MOD": "naming_fsmo", - "DOMAINDN_MOD": "pdc_fsmo", - "MODULES_LIST": ",".join(modules_list), - "TDB_MODULES_LIST": tdb_modules_list_as_string, - "MODULES_LIST2": ",".join(modules_list2), - "BACKEND_MOD": ",".join(backend_modules), + "SCHEMADN": ldb.Dn(schema.ldb, names.schemadn).get_casefold(), + "CONFIGDN": ldb.Dn(schema.ldb, names.configdn).get_casefold(), + "DOMAINDN": ldb.Dn(schema.ldb, names.domaindn).get_casefold(), + "LDAP_BACKEND_LINE": ldap_backend_line, }) - samdb.load_ldif_file_add(setup_path("provision_init.ldif")) + + setup_add_ldif(samdb, setup_path("provision_init.ldif"), { + "BACKEND_TYPE": provision_backend.type, + "SERVER_ROLE": serverrole + }) message("Setting up sam.ldb rootDSE") setup_samdb_rootdse(samdb, setup_path, names) @@ -711,9 +578,10 @@ def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info, raise samdb.transaction_commit() - + + def secretsdb_self_join(secretsdb, domain, - netbiosname, domainsid, machinepass, + netbiosname, machinepass, domainsid=None, realm=None, dnsdomain=None, keytab_path=None, key_version_number=1, @@ -748,18 +616,19 @@ def secretsdb_self_join(secretsdb, domain, msg["secret"] = [machinepass] msg["samAccountName"] = ["%s$" % netbiosname] msg["secureChannelType"] = [str(secure_channel_type)] - msg["objectSid"] = [ndr_pack(domainsid)] + if domainsid is not None: + msg["objectSid"] = [ndr_pack(domainsid)] res = secretsdb.search(base="cn=Primary Domains", attrs=attrs, expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain))" % (domain, realm, str(domainsid))), - scope=SCOPE_ONELEVEL) + 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=SCOPE_BASE) + res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE) if len(res) == 1: msg["priorSecret"] = res[0]["secret"] @@ -778,7 +647,8 @@ def secretsdb_self_join(secretsdb, domain, secretsdb.add(msg) -def secretsdb_setup_dns(secretsdb, setup_path, realm, dnsdomain, +def secretsdb_setup_dns(secretsdb, setup_path, private_dir, + realm, dnsdomain, dns_keytab_path, dnspass): """Add DNS specific bits to a secrets database. @@ -786,6 +656,11 @@ def secretsdb_setup_dns(secretsdb, setup_path, realm, dnsdomain, :param setup_path: Setup path function :param machinepass: Machine password """ + try: + os.unlink(os.path.join(private_dir, dns_keytab_path)) + except OSError: + pass + setup_ldb(secretsdb, setup_path("secrets_dns.ldif"), { "REALM": realm, "DNSDOMAIN": dnsdomain, @@ -794,7 +669,7 @@ def secretsdb_setup_dns(secretsdb, setup_path, realm, dnsdomain, }) -def setup_secretsdb(path, setup_path, session_info, credentials, lp): +def setup_secretsdb(path, setup_path, session_info, backend_credentials, lp): """Setup the secrets database. :param path: Path to the secrets database. @@ -806,30 +681,47 @@ def setup_secretsdb(path, setup_path, session_info, credentials, lp): """ if os.path.exists(path): os.unlink(path) - secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials, + secrets_ldb = Ldb(path, session_info=session_info, lp=lp) secrets_ldb.erase() secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif")) - secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials, + 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 credentials is not None and credentials.authentication_requested(): - if credentials.get_bind_dn() is not None: + 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": credentials.get_bind_dn(), - "LDAPMANAGERPASS_B64": b64encode(credentials.get_password()) + "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": credentials.get_username(), - "LDAPADMINREALM": credentials.get_realm(), - "LDAPADMINPASS_B64": b64encode(credentials.get_password()) + "LDAPADMINUSER": backend_credentials.get_username(), + "LDAPADMINREALM": backend_credentials.get_realm(), + "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password()) }) return secrets_ldb +def setup_privileges(path, setup_path, session_info, lp): + """Setup the privileges database. + + :param path: Path to the privileges database. + :param setup_path: Get the path to a setup file. + :param session_info: Session info. + :param credentials: Credentials + :param lp: Loadparm context + :return: LDB handle for the created secrets database + """ + if os.path.exists(path): + os.unlink(path) + privilege_ldb = Ldb(path, session_info=session_info, lp=lp) + privilege_ldb.erase() + privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif")) + + def setup_registry(path, setup_path, session_info, lp): """Setup the registry. @@ -908,9 +800,9 @@ def setup_self_join(samdb, names, "DEFAULTSITE": names.sitename, "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain), "MACHINEPASS_B64": b64encode(machinepass), - "DNSPASS_B64": b64encode(dnspass), "REALM": names.realm, "DOMAIN": names.domain, + "DOMAINSID": str(domainsid), "DNSDOMAIN": names.dnsdomain, "SAMBA_VERSION_STRING": version, "NTDSGUID": ntdsguid_line, @@ -926,7 +818,7 @@ def setup_self_join(samdb, names, # 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) names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID", - expression="", scope=SCOPE_BASE) + expression="", scope=ldb.SCOPE_BASE) assert isinstance(names.ntdsguid, str) # Setup fSMORoleOwner entries to point at the newly created DC entry @@ -939,17 +831,36 @@ def setup_self_join(samdb, names, "DEFAULTSITE": names.sitename, "SERVERDN": names.serverdn, "NETBIOSNAME": names.netbiosname, - "NTDSGUID": names.ntdsguid + "NTDSGUID": names.ntdsguid, + "DNSPASS_B64": b64encode(dnspass), }) -def setup_samdb(path, setup_path, session_info, credentials, lp, +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) + 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, ldap_backend=None): + schema=None): """Setup a complete SAM Database. :note: This will wipe the main SAM database file! @@ -962,7 +873,7 @@ def setup_samdb(path, setup_path, session_info, credentials, lp, if dom_for_fun_level is None: dom_for_fun_level = DS_DOMAIN_FUNCTION_2003 if dom_for_fun_level < DS_DOMAIN_FUNCTION_2003: - raise ProvisioningError("You want to run SAMBA 4 on a domain and forest function level lower than Windows 2003 (Native). This isn't supported!") + 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!") @@ -972,17 +883,16 @@ def setup_samdb(path, setup_path, session_info, credentials, lp, # Also wipes the database setup_samdb_partitions(path, setup_path, message=message, lp=lp, - credentials=credentials, session_info=session_info, - names=names, ldap_backend=ldap_backend, - serverrole=serverrole) + 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, - sambadn=names.sambadn, ldap_backend_type=ldap_backend.ldap_backend_type) + 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=credentials, lp=lp) + credentials=provision_backend.credentials, lp=lp) message("Pre-loading the Samba 4 and AD schema") @@ -992,21 +902,11 @@ def setup_samdb(path, setup_path, session_info, credentials, lp, # And now we can connect to the DB - the schema won't be loaded from the DB samdb.connect(path) - # Load @OPTIONS - samdb.load_ldif_file_add(setup_path("provision_options.ldif")) - if fill == FILL_DRS: return samdb - + samdb.transaction_start() try: - message("Erasing data from partitions") - # Load the schema (again). This time it will force a reindex, - # and will therefore make the erase_partitions() below - # computationally sane - samdb.set_schema_from_ldb(schema.ldb) - samdb.erase_partitions() - # Set the domain functionality levels onto the database. # Various module (the password_hash module in particular) need # to know what level of AD we are emulating. @@ -1018,8 +918,7 @@ def setup_samdb(path, setup_path, session_info, credentials, lp, samdb.set_opaque_integer("domainControllerFunctionality", domainControllerFunctionality) samdb.set_domain_sid(str(domainsid)) - if serverrole == "domain controller": - samdb.set_invocation_id(invocationid) + samdb.set_invocation_id(invocationid) message("Adding DomainDN: %s" % names.domaindn) @@ -1030,9 +929,12 @@ def setup_samdb(path, setup_path, session_info, credentials, lp, domainguid_line = "objectGUID: %s\n-" % domainguid else: domainguid_line = "" + + descr = b64encode(get_domain_descriptor(domainsid)) setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), { "DOMAINDN": names.domaindn, - "DOMAINGUID": domainguid_line + "DOMAINGUID": domainguid_line, + "DESCRIPTOR": descr }) @@ -1051,16 +953,11 @@ def setup_samdb(path, setup_path, session_info, credentials, lp, }) message("Adding configuration container") - descr = get_config_descriptor(domainsid); + descr = b64encode(get_config_descriptor(domainsid)) setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), { "CONFIGDN": names.configdn, "DESCRIPTOR": descr, }) - message("Modifying configuration container") - setup_modify_ldif(samdb, setup_path("provision_configuration_basedn_modify.ldif"), { - "CONFIGDN": names.configdn, - "SCHEMADN": names.schemadn, - }) # The LDIF here was created when the Schema object was constructed message("Setting up sam.ldb schema") @@ -1071,6 +968,14 @@ def setup_samdb(path, setup_path, session_info, credentials, lp, setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"), {"SCHEMADN": names.schemadn}) + message("Reopening sam.ldb with new schema"); + 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") setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), { "CONFIGDN": names.configdn, @@ -1113,6 +1018,12 @@ def setup_samdb(path, setup_path, session_info, credentials, lp, "POLICYGUID_DC": policyguid_dc }) + setup_modify_ldif(samdb, setup_path("provision_basedn_references.ldif"), { + "DOMAINDN": names.domaindn}) + + setup_modify_ldif(samdb, setup_path("provision_configuration_references.ldif"), { + "CONFIGDN": names.configdn, + "SCHEMADN": names.schemadn}) if fill == FILL_FULL: message("Setting up sam.ldb users and groups") setup_add_ldif(samdb, setup_path("provision_users.ldif"), { @@ -1123,21 +1034,20 @@ def setup_samdb(path, setup_path, session_info, credentials, lp, "KRBTGTPASS_B64": b64encode(krbtgtpass), }) - if serverrole == "domain controller": - message("Setting up self join") - setup_self_join(samdb, names=names, invocationid=invocationid, - dnspass=dnspass, - machinepass=machinepass, - domainsid=domainsid, 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) - names.ntdsguid = samdb.searchone(basedn=ntds_dn, - attribute="objectGUID", expression="", scope=SCOPE_BASE) - assert isinstance(names.ntdsguid, str) + message("Setting up self join") + setup_self_join(samdb, names=names, invocationid=invocationid, + dnspass=dnspass, + machinepass=machinepass, + domainsid=domainsid, 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) + names.ntdsguid = samdb.searchone(basedn=ntds_dn, + attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE) + assert isinstance(names.ntdsguid, str) except: samdb.transaction_cancel() @@ -1150,6 +1060,49 @@ def setup_samdb(path, setup_path, session_info, credentials, lp, FILL_FULL = "FULL" FILL_NT4SYNC = "NT4SYNC" FILL_DRS = "DRS" +SYSVOL_ACL = "O:${DOMAINSID}-500G:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;S-1-5-32-549)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)" +POLICIES_ACL = "O:${DOMAINSID}-500G:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;S-1-5-32-549)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)(A;OICI;0x001301bf;;;${DOMAINSID}-520)" + +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 + acl = SYSVOL_ACL.replace("${DOMAINSID}",str(domainsid)) + try: + os.chown(sysvol,-1,gid) + except: + canchown = 0 + + setntacl(lp,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),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),acl,str(domainsid)) + + # Set ACL for GPO + policy_path = os.path.join(sysvol, names.dnsdomain, "Policies") + acl = POLICIES_ACL.replace("${DOMAINSID}",str(domainsid)) + set_gpo_acl(policy_path,dsacl2fsacl(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, @@ -1165,11 +1118,11 @@ def provision(setup_dir, message, session_info, dnspass=None, root=None, nobody=None, users=None, wheel=None, backup=None, aci=None, serverrole=None, dom_for_fun_level=None, - ldap_backend_extra_port=None, ldap_backend_type=None, + ldap_backend_extra_port=None, backend_type=None, sitename=None, ol_mmr_urls=None, ol_olc=None, setup_ds_path=None, slapd_path=None, nosync=False, - ldap_dryrun_mode=False): + ldap_dryrun_mode=False,useeadb=False): """Provision samba4 :note: caution, this wipes all existing data! @@ -1203,6 +1156,12 @@ def provision(setup_dir, message, session_info, #Make a new, random password between Samba and it's LDAP server ldapadminpass=glue.generate_random_str(12) + if backend_type is None: + backend_type = "ldb" + + sid_generator = "internal" + if backend_type == "fedora-ds": + sid_generator = "backend" root_uid = findnss_uid([root or "root"]) nobody_uid = findnss_uid([nobody or "nobody"]) @@ -1211,6 +1170,10 @@ def provision(setup_dir, message, session_info, wheel_gid = findnss_gid(["wheel", "adm"]) else: wheel_gid = findnss_gid([wheel]) + try: + bind_gid = findnss_gid(["bind", "named"]) + except KeyError: + bind_gid = None if targetdir is not None: if (not os.path.exists(os.path.join(targetdir, "etc"))): @@ -1220,20 +1183,31 @@ def provision(setup_dir, message, session_info, smbconf = param.default_path() # only install a new smb.conf if there isn't one there already - if not os.path.exists(smbconf): + if os.path.exists(smbconf): + # if Samba Team members can't figure out the weird errors + # loading an empty smb.conf gives, then we need to be smarter. + # Pretend it just didn't exist --abartlet + 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) + else: make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole, - targetdir) + targetdir, sid_generator, useeadb) lp = param.LoadParm() lp.load(smbconf) - names = guess_names(lp=lp, hostname=hostname, domain=domain, - dnsdomain=realm, serverrole=serverrole, sitename=sitename, - rootdn=rootdn, domaindn=domaindn, configdn=configdn, schemadn=schemadn, - serverdn=serverdn) + names = guess_names(lp=lp, hostname=hostname, domain=domain, + dnsdomain=realm, serverrole=serverrole, + domaindn=domaindn, configdn=configdn, schemadn=schemadn, + serverdn=serverdn, sitename=sitename) paths = provision_paths_from_lp(lp, names.dnsdomain) + 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] @@ -1250,65 +1224,94 @@ def provision(setup_dir, message, session_info, serverrole = lp.get("server role") assert serverrole in ("domain controller", "member server", "standalone") - if invocationid is None and serverrole == "domain controller": + if invocationid is None: invocationid = str(uuid.uuid4()) 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")) 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, schemadn=names.schemadn, serverdn=names.serverdn, - sambadn=names.sambadn, ldap_backend_type=ldap_backend_type) - - secrets_credentials = credentials - provision_backend = None - if ldap_backend_type: - # We only support an LDAP backend over ldapi:// - - provision_backend = ProvisionBackend(paths=paths, setup_path=setup_path, - lp=lp, credentials=credentials, - names=names, - message=message, hostname=hostname, - root=root, schema=schema, - ldap_backend_type=ldap_backend_type, - ldapadminpass=ldapadminpass, - ldap_backend_extra_port=ldap_backend_extra_port, - ol_mmr_urls=ol_mmr_urls, - slapd_path=slapd_path, - setup_ds_path=setup_ds_path, - ldap_dryrun_mode=ldap_dryrun_mode) - - # Now use the backend credentials to access the databases - credentials = provision_backend.credentials - secrets_credentials = provision_backend.adminCredentials - ldapi_url = provision_backend.ldapi_uri + if backend_type == "ldb": + provision_backend = LDBBackend(backend_type, + paths=paths, setup_path=setup_path, + lp=lp, credentials=credentials, + names=names, + message=message) + elif backend_type == "existing": + provision_backend = ExistingBackend(backend_type, + paths=paths, setup_path=setup_path, + lp=lp, credentials=credentials, + names=names, + message=message) + elif backend_type == "fedora-ds": + provision_backend = FDSBackend(backend_type, + paths=paths, setup_path=setup_path, + lp=lp, credentials=credentials, + names=names, + message=message, + domainsid=domainsid, + schema=schema, + hostname=hostname, + ldapadminpass=ldapadminpass, + slapd_path=slapd_path, + ldap_backend_extra_port=ldap_backend_extra_port, + ldap_dryrun_mode=ldap_dryrun_mode, + root=root, + setup_ds_path=setup_ds_path) + elif backend_type == "openldap": + provision_backend = OpenLDAPBackend(backend_type, + paths=paths, setup_path=setup_path, + lp=lp, credentials=credentials, + names=names, + message=message, + domainsid=domainsid, + schema=schema, + hostname=hostname, + ldapadminpass=ldapadminpass, + slapd_path=slapd_path, + ldap_backend_extra_port=ldap_backend_extra_port, + ldap_dryrun_mode=ldap_dryrun_mode, + ol_mmr_urls=ol_mmr_urls, + nosync=nosync) + else: + raise ProvisioningError("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") share_ldb = Ldb(paths.shareconf, session_info=session_info, - credentials=credentials, lp=lp) + 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, - credentials=secrets_credentials, lp=lp) + 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=session_info, - credentials=credentials, lp=lp, names=names, - message=message, + 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, @@ -1317,8 +1320,7 @@ def provision(setup_dir, message, session_info, invocationid=invocationid, machinepass=machinepass, dnspass=dnspass, ntdsguid=ntdsguid, serverrole=serverrole, - dom_for_fun_level=dom_for_fun_level, - ldap_backend=provision_backend) + dom_for_fun_level=dom_for_fun_level) if serverrole == "domain controller": if paths.netlogon is None: @@ -1333,23 +1335,6 @@ def provision(setup_dir, message, session_info, (paths.smbconf, setup_path("provision.smb.conf.dc"))) assert(paths.sysvol is not None) - # Set up group policies (domain policy and domain controller policy) - - policy_path = os.path.join(paths.sysvol, names.dnsdomain, "Policies", - "{" + policyguid + "}") - 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) if not os.path.isdir(paths.netlogon): os.makedirs(paths.netlogon, 0755) @@ -1359,20 +1344,25 @@ def provision(setup_dir, message, session_info, 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,names,samdb,policyguid,policyguid_dc,domainsid) + setsysvolacl(samdb,names,paths.netlogon,paths.sysvol,wheel_gid,domainsid,lp) + message("Setting up sam.ldb rootDSE marking as synchronized") setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif")) - # Only make a zone file on the first DC, it should be replicated with DNS replication + 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_self_join(secrets_ldb, domain=domain, - realm=names.realm, - dnsdomain=names.dnsdomain, - netbiosname=names.netbiosname, - domainsid=domainsid, - machinepass=machinepass, - secure_channel_type=SEC_CHAN_BDC) - - secretsdb_setup_dns(secrets_ldb, setup_path, + secretsdb_setup_dns(secrets_ldb, setup_path, + paths.private_dir, realm=names.realm, dnsdomain=names.dnsdomain, dns_keytab_path=paths.dns_keytab, dnspass=dnspass) @@ -1380,13 +1370,15 @@ def provision(setup_dir, message, session_info, domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID") assert isinstance(domainguid, str) - create_zone_file(paths.dns, setup_path, dnsdomain=names.dnsdomain, + # Only make a zone file on the first DC, it should be replicated + # with DNS replication + create_zone_file(message, paths, setup_path, dnsdomain=names.dnsdomain, hostip=hostip, hostip6=hostip6, hostname=names.hostname, realm=names.realm, domainguid=domainguid, ntdsguid=names.ntdsguid) - create_named_conf(paths.namedconf, setup_path, realm=names.realm, + 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, @@ -1400,60 +1392,24 @@ def provision(setup_dir, message, session_info, 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) + #Now commit the secrets.ldb to disk secrets_ldb.transaction_commit() - if provision_backend is not None: - if ldap_backend_type == "fedora-ds": - ldapi_db = Ldb(provision_backend.ldapi_uri, lp=lp, credentials=credentials) - - # delete default SASL mappings - res = ldapi_db.search(expression="(!(cn=samba-admin mapping))", base="cn=mapping,cn=sasl,cn=config", scope=SCOPE_ONELEVEL, attrs=["dn"]) - - # configure in-directory access control on Fedora DS via the aci attribute (over a direct ldapi:// socket) - for i in range (0, len(res)): - dn = str(res[i]["dn"]) - ldapi_db.delete(dn) - - aci = """(targetattr = "*") (version 3.0;acl "full access to all by samba-admin";allow (all)(userdn = "ldap:///CN=samba-admin,%s");)""" % names.sambadn - - m = ldb.Message() - m["aci"] = ldb.MessageElement([aci], ldb.FLAG_MOD_REPLACE, "aci") - - m.dn = ldb.Dn(1, names.domaindn) - ldapi_db.modify(m) - - m.dn = ldb.Dn(1, names.configdn) - ldapi_db.modify(m) - - m.dn = ldb.Dn(1, names.schemadn) - ldapi_db.modify(m) - - # if an LDAP backend is in use, terminate slapd after final provision and check its proper termination - if provision_backend.slapd.poll() is None: - #Kill the slapd - if hasattr(provision_backend.slapd, "terminate"): - provision_backend.slapd.terminate() - else: - # Older python versions don't have .terminate() - import signal - os.kill(provision_backend.slapd.pid, signal.SIGTERM) - - #and now wait for it to die - provision_backend.slapd.communicate() - - # 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:") - slapd_command = "\'" + "\' \'".join(provision_backend.slapd_command) + "\'" - message(slapd_command) - message("This slapd-Commandline is also stored under: " + paths.ldapdir + "/ldap_backend_startup.sh") + # the commit creates the dns.keytab, now chown it + dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab) + if (os.path.isfile(dns_keytab_path) and paths.bind_gid is not None): + try: + 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)) - setup_file(setup_path("ldap_backend_startup.sh"), paths.ldapdir + "/ldap_backend_startup.sh", { - "SLAPD_COMMAND" : slapd_command}) - - - create_phpldapadmin_config(paths.phpldapadminconfig, setup_path, - ldapi_url) message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig) @@ -1464,15 +1420,22 @@ def provision(setup_dir, message, session_info, message("DNS Domain: %s" % names.dnsdomain) message("DOMAIN SID: %s" % str(domainsid)) if samdb_fill == FILL_FULL: - message("Admin password: %s" % adminpass) - if provision_backend: + message("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()) else: message("LDAP Admin User: %s" % provision_backend.credentials.get_username()) message("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") + + result = ProvisionResult() result.domaindn = domaindn result.paths = paths @@ -1510,510 +1473,6 @@ def provision_become_dc(setup_dir=None, sitename=sitename) -def setup_db_config(setup_path, dbdir): - """Setup a Berkeley database. - - :param setup_path: Setup path function. - :param dbdir: Database directory.""" - if not os.path.isdir(os.path.join(dbdir, "bdb-logs")): - os.makedirs(os.path.join(dbdir, "bdb-logs"), 0700) - if not os.path.isdir(os.path.join(dbdir, "tmp")): - os.makedirs(os.path.join(dbdir, "tmp"), 0700) - - setup_file(setup_path("DB_CONFIG"), os.path.join(dbdir, "DB_CONFIG"), - {"LDAPDBDIR": dbdir}) - -class ProvisionBackend(object): - def __init__(self, paths=None, setup_path=None, lp=None, credentials=None, - names=None, message=None, - hostname=None, root=None, - schema=None, ldapadminpass=None, - ldap_backend_type=None, ldap_backend_extra_port=None, - ol_mmr_urls=None, - setup_ds_path=None, slapd_path=None, - nosync=False, ldap_dryrun_mode=False): - """Provision an LDAP backend for samba4 - - This works for OpenLDAP and Fedora DS - """ - - self.ldapi_uri = "ldapi://" + urllib.quote(os.path.join(paths.ldapdir, "ldapi"), safe="") - - if not os.path.isdir(paths.ldapdir): - os.makedirs(paths.ldapdir, 0700) - - if ldap_backend_type == "existing": - #Check to see that this 'existing' LDAP backend in fact exists - ldapi_db = Ldb(self.ldapi_uri, credentials=credentials) - search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE, - expression="(objectClass=OpenLDAProotDSE)") - - # If we have got here, then we must have a valid connection to the LDAP server, with valid credentials supplied - # This caused them to be set into the long-term database later in the script. - self.credentials = credentials - self.ldap_backend_type = "openldap" #For now, assume existing backends at least emulate OpenLDAP - return - - # we will shortly start slapd with ldapi for final provisioning. first check with ldapsearch -> rootDSE via self.ldapi_uri - # if another instance of slapd is already running - try: - ldapi_db = Ldb(self.ldapi_uri) - search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE, - expression="(objectClass=OpenLDAProotDSE)"); - try: - f = open(paths.slapdpid, "r") - p = f.read() - f.close() - message("Check for slapd Process with PID: " + str(p) + " and terminate it manually.") - except: - pass - - raise ProvisioningError("Warning: Another slapd Instance seems already running on this host, listening to " + self.ldapi_uri + ". Please shut it down before you continue. ") - - except LdbError, e: - pass - - # Try to print helpful messages when the user has not specified the path to slapd - if slapd_path is None: - raise ProvisioningError("Warning: LDAP-Backend must be setup with path to slapd, e.g. --slapd-path=\"/usr/local/libexec/slapd\"!") - if not os.path.exists(slapd_path): - message (slapd_path) - raise ProvisioningError("Warning: Given Path to slapd does not exist!") - - schemadb_path = os.path.join(paths.ldapdir, "schema-tmp.ldb") - try: - os.unlink(schemadb_path) - except OSError: - pass - - - # Put the LDIF of the schema into a database so we can search on - # it to generate schema-dependent configurations in Fedora DS and - # OpenLDAP - os.path.join(paths.ldapdir, "schema-tmp.ldb") - schema.ldb.connect(schemadb_path) - schema.ldb.transaction_start() - - # These bits of LDIF are supplied when the Schema object is created - schema.ldb.add_ldif(schema.schema_dn_add) - schema.ldb.modify_ldif(schema.schema_dn_modify) - schema.ldb.add_ldif(schema.schema_data) - schema.ldb.transaction_commit() - - self.credentials = Credentials() - self.credentials.guess(lp) - #Kerberos to an ldapi:// backend makes no sense - self.credentials.set_kerberos_state(DONT_USE_KERBEROS) - - self.adminCredentials = Credentials() - self.adminCredentials.guess(lp) - #Kerberos to an ldapi:// backend makes no sense - self.adminCredentials.set_kerberos_state(DONT_USE_KERBEROS) - - self.ldap_backend_type = ldap_backend_type - - if ldap_backend_type == "fedora-ds": - provision_fds_backend(self, paths=paths, setup_path=setup_path, - names=names, message=message, - hostname=hostname, - ldapadminpass=ldapadminpass, root=root, - schema=schema, - ldap_backend_extra_port=ldap_backend_extra_port, - setup_ds_path=setup_ds_path, - slapd_path=slapd_path, - nosync=nosync, - ldap_dryrun_mode=ldap_dryrun_mode) - - elif ldap_backend_type == "openldap": - provision_openldap_backend(self, paths=paths, setup_path=setup_path, - names=names, message=message, - hostname=hostname, - ldapadminpass=ldapadminpass, root=root, - schema=schema, - ldap_backend_extra_port=ldap_backend_extra_port, - ol_mmr_urls=ol_mmr_urls, - slapd_path=slapd_path, - nosync=nosync, - ldap_dryrun_mode=ldap_dryrun_mode) - else: - raise ProvisioningError("Unknown LDAP backend type selected") - - self.credentials.set_password(ldapadminpass) - self.adminCredentials.set_username("samba-admin") - self.adminCredentials.set_password(ldapadminpass) - - # Now start the slapd, so we can provision onto it. We keep the - # subprocess context around, to kill this off at the successful - # end of the script - self.slapd = subprocess.Popen(self.slapd_provision_command, close_fds=True, shell=False) - - while self.slapd.poll() is None: - # Wait until the socket appears - try: - ldapi_db = Ldb(self.ldapi_uri, lp=lp, credentials=self.credentials) - search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE, - expression="(objectClass=OpenLDAProotDSE)") - # If we have got here, then we must have a valid connection to the LDAP server! - return - except LdbError, e: - time.sleep(1) - pass - - raise ProvisioningError("slapd died before we could make a connection to it") - - -def provision_openldap_backend(result, paths=None, setup_path=None, names=None, - message=None, - hostname=None, ldapadminpass=None, root=None, - schema=None, - ldap_backend_extra_port=None, - ol_mmr_urls=None, - slapd_path=None, nosync=False, - ldap_dryrun_mode=False): - - #Allow the test scripts to turn off fsync() for OpenLDAP as for TDB and LDB - nosync_config = "" - if nosync: - nosync_config = "dbnosync" - - lnkattr = get_linked_attributes(names.schemadn,schema.ldb) - refint_attributes = "" - memberof_config = "# Generated from Samba4 schema\n" - for att in lnkattr.keys(): - if lnkattr[att] is not None: - refint_attributes = refint_attributes + " " + att - - memberof_config += read_and_sub_file(setup_path("memberof.conf"), - { "MEMBER_ATTR" : att , - "MEMBEROF_ATTR" : lnkattr[att] }) - - refint_config = read_and_sub_file(setup_path("refint.conf"), - { "LINK_ATTRS" : refint_attributes}) - - attrs = ["linkID", "lDAPDisplayName"] - res = schema.ldb.search(expression="(&(objectclass=attributeSchema)(searchFlags:1.2.840.113556.1.4.803:=1))", base=names.schemadn, scope=SCOPE_ONELEVEL, attrs=attrs) - index_config = "" - for i in range (0, len(res)): - index_attr = res[i]["lDAPDisplayName"][0] - if index_attr == "objectGUID": - index_attr = "entryUUID" - - index_config += "index " + index_attr + " eq\n" - -# generate serverids, ldap-urls and syncrepl-blocks for mmr hosts - mmr_on_config = "" - mmr_replicator_acl = "" - mmr_serverids_config = "" - mmr_syncrepl_schema_config = "" - mmr_syncrepl_config_config = "" - mmr_syncrepl_user_config = "" - - - if ol_mmr_urls is not None: - # For now, make these equal - mmr_pass = ldapadminpass - - url_list=filter(None,ol_mmr_urls.split(' ')) - if (len(url_list) == 1): - url_list=filter(None,ol_mmr_urls.split(',')) - - - mmr_on_config = "MirrorMode On" - mmr_replicator_acl = " by dn=cn=replicator,cn=samba read" - serverid=0 - for url in url_list: - serverid=serverid+1 - mmr_serverids_config += read_and_sub_file(setup_path("mmr_serverids.conf"), - { "SERVERID" : str(serverid), - "LDAPSERVER" : url }) - rid=serverid*10 - rid=rid+1 - mmr_syncrepl_schema_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"), - { "RID" : str(rid), - "MMRDN": names.schemadn, - "LDAPSERVER" : url, - "MMR_PASSWORD": mmr_pass}) - - rid=rid+1 - mmr_syncrepl_config_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"), - { "RID" : str(rid), - "MMRDN": names.configdn, - "LDAPSERVER" : url, - "MMR_PASSWORD": mmr_pass}) - - rid=rid+1 - mmr_syncrepl_user_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"), - { "RID" : str(rid), - "MMRDN": names.domaindn, - "LDAPSERVER" : url, - "MMR_PASSWORD": mmr_pass }) - # OpenLDAP cn=config initialisation - olc_syncrepl_config = "" - olc_mmr_config = "" - # if mmr = yes, generate cn=config-replication directives - # and olc_seed.lif for the other mmr-servers - if ol_mmr_urls is not None: - serverid=0 - olc_serverids_config = "" - olc_syncrepl_seed_config = "" - olc_mmr_config += read_and_sub_file(setup_path("olc_mmr.conf"),{}) - rid=1000 - for url in url_list: - serverid=serverid+1 - olc_serverids_config += read_and_sub_file(setup_path("olc_serverid.conf"), - { "SERVERID" : str(serverid), - "LDAPSERVER" : url }) - - rid=rid+1 - olc_syncrepl_config += read_and_sub_file(setup_path("olc_syncrepl.conf"), - { "RID" : str(rid), - "LDAPSERVER" : url, - "MMR_PASSWORD": mmr_pass}) - - olc_syncrepl_seed_config += read_and_sub_file(setup_path("olc_syncrepl_seed.conf"), - { "RID" : str(rid), - "LDAPSERVER" : url}) - - setup_file(setup_path("olc_seed.ldif"), paths.olcseedldif, - {"OLC_SERVER_ID_CONF": olc_serverids_config, - "OLC_PW": ldapadminpass, - "OLC_SYNCREPL_CONF": olc_syncrepl_seed_config}) - # end olc - - setup_file(setup_path("slapd.conf"), paths.slapdconf, - {"DNSDOMAIN": names.dnsdomain, - "LDAPDIR": paths.ldapdir, - "DOMAINDN": names.domaindn, - "CONFIGDN": names.configdn, - "SCHEMADN": names.schemadn, - "MEMBEROF_CONFIG": memberof_config, - "MIRRORMODE": mmr_on_config, - "REPLICATOR_ACL": mmr_replicator_acl, - "MMR_SERVERIDS_CONFIG": mmr_serverids_config, - "MMR_SYNCREPL_SCHEMA_CONFIG": mmr_syncrepl_schema_config, - "MMR_SYNCREPL_CONFIG_CONFIG": mmr_syncrepl_config_config, - "MMR_SYNCREPL_USER_CONFIG": mmr_syncrepl_user_config, - "OLC_SYNCREPL_CONFIG": olc_syncrepl_config, - "OLC_MMR_CONFIG": olc_mmr_config, - "REFINT_CONFIG": refint_config, - "INDEX_CONFIG": index_config, - "NOSYNC": nosync_config}) - - setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "user")) - setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "config")) - setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "schema")) - - if not os.path.exists(os.path.join(paths.ldapdir, "db", "samba", "cn=samba")): - os.makedirs(os.path.join(paths.ldapdir, "db", "samba", "cn=samba"), 0700) - - setup_file(setup_path("cn=samba.ldif"), - os.path.join(paths.ldapdir, "db", "samba", "cn=samba.ldif"), - { "UUID": str(uuid.uuid4()), - "LDAPTIME": timestring(int(time.time()))} ) - setup_file(setup_path("cn=samba-admin.ldif"), - os.path.join(paths.ldapdir, "db", "samba", "cn=samba", "cn=samba-admin.ldif"), - {"LDAPADMINPASS_B64": b64encode(ldapadminpass), - "UUID": str(uuid.uuid4()), - "LDAPTIME": timestring(int(time.time()))} ) - - if ol_mmr_urls is not None: - setup_file(setup_path("cn=replicator.ldif"), - os.path.join(paths.ldapdir, "db", "samba", "cn=samba", "cn=replicator.ldif"), - {"MMR_PASSWORD_B64": b64encode(mmr_pass), - "UUID": str(uuid.uuid4()), - "LDAPTIME": timestring(int(time.time()))} ) - - - mapping = "schema-map-openldap-2.3" - backend_schema = "backend-schema.schema" - - backend_schema_data = schema.ldb.convert_schema_to_openldap("openldap", open(setup_path(mapping), 'r').read()) - assert backend_schema_data is not None - open(os.path.join(paths.ldapdir, backend_schema), 'w').write(backend_schema_data) - - # now we generate the needed strings to start slapd automatically, - # first ldapi_uri... - if ldap_backend_extra_port is not None: - # When we use MMR, we can't use 0.0.0.0 as it uses the name - # specified there as part of it's clue as to it's own name, - # and not to replicate to itself - if ol_mmr_urls is None: - server_port_string = "ldap://0.0.0.0:%d" % ldap_backend_extra_port - else: - server_port_string = "ldap://" + names.hostname + "." + names.dnsdomain +":%d" % ldap_backend_extra_port - else: - server_port_string = "" - - # Prepare the 'result' information - the commands to return in particular - result.slapd_provision_command = [slapd_path] - - result.slapd_provision_command.append("-F" + paths.olcdir) - - result.slapd_provision_command.append("-h") - - # copy this command so we have two version, one with -d0 and only ldapi, and one with all the listen commands - result.slapd_command = list(result.slapd_provision_command) - - result.slapd_provision_command.append(result.ldapi_uri) - result.slapd_provision_command.append("-d0") - - uris = result.ldapi_uri - if server_port_string is not "": - uris = uris + " " + server_port_string - - result.slapd_command.append(uris) - - # Set the username - done here because Fedora DS still uses the admin DN and simple bind - result.credentials.set_username("samba-admin") - - # If we were just looking for crashes up to this point, it's a - # good time to exit before we realise we don't have OpenLDAP on - # this system - if ldap_dryrun_mode: - sys.exit(0) - - # Finally, convert the configuration into cn=config style! - if not os.path.isdir(paths.olcdir): - os.makedirs(paths.olcdir, 0770) - - retcode = subprocess.call([slapd_path, "-Ttest", "-f", paths.slapdconf, "-F", paths.olcdir], close_fds=True, shell=False) - -# We can't do this, as OpenLDAP is strange. It gives an error -# output to the above, but does the conversion sucessfully... -# -# if retcode != 0: -# raise ProvisioningError("conversion from slapd.conf to cn=config failed") - - if not os.path.exists(os.path.join(paths.olcdir, "cn=config.ldif")): - raise ProvisioningError("conversion from slapd.conf to cn=config failed") - - # Don't confuse the admin by leaving the slapd.conf around - os.remove(paths.slapdconf) - - -def provision_fds_backend(result, paths=None, setup_path=None, names=None, - message=None, - hostname=None, ldapadminpass=None, root=None, - schema=None, - ldap_backend_extra_port=None, - setup_ds_path=None, - slapd_path=None, - nosync=False, - ldap_dryrun_mode=False): - - if ldap_backend_extra_port is not None: - serverport = "ServerPort=%d" % ldap_backend_extra_port - else: - serverport = "" - - setup_file(setup_path("fedorads.inf"), paths.fedoradsinf, - {"ROOT": root, - "HOSTNAME": hostname, - "DNSDOMAIN": names.dnsdomain, - "LDAPDIR": paths.ldapdir, - "DOMAINDN": names.domaindn, - "LDAPMANAGERDN": names.ldapmanagerdn, - "LDAPMANAGERPASS": ldapadminpass, - "SERVERPORT": serverport}) - - setup_file(setup_path("fedorads-partitions.ldif"), paths.fedoradspartitions, - {"CONFIGDN": names.configdn, - "SCHEMADN": names.schemadn, - "SAMBADN": names.sambadn, - }) - - setup_file(setup_path("fedorads-sasl.ldif"), paths.fedoradssasl, - {"SAMBADN": names.sambadn, - }) - - setup_file(setup_path("fedorads-pam.ldif"), paths.fedoradspam) - - lnkattr = get_linked_attributes(names.schemadn,schema.ldb) - - refint_config = data = open(setup_path("fedorads-refint-delete.ldif"), 'r').read() - memberof_config = "" - index_config = "" - argnum = 3 - - for attr in lnkattr.keys(): - if lnkattr[attr] is not None: - refint_config += read_and_sub_file(setup_path("fedorads-refint-add.ldif"), - { "ARG_NUMBER" : str(argnum) , - "LINK_ATTR" : attr }) - memberof_config += read_and_sub_file(setup_path("fedorads-linked-attributes.ldif"), - { "MEMBER_ATTR" : attr , - "MEMBEROF_ATTR" : lnkattr[attr] }) - index_config += read_and_sub_file(setup_path("fedorads-index.ldif"), - { "ATTR" : attr }) - argnum += 1 - - open(paths.fedoradsrefint, 'w').write(refint_config) - open(paths.fedoradslinkedattributes, 'w').write(memberof_config) - - attrs = ["lDAPDisplayName"] - res = schema.ldb.search(expression="(&(objectclass=attributeSchema)(searchFlags:1.2.840.113556.1.4.803:=1))", base=names.schemadn, scope=SCOPE_ONELEVEL, attrs=attrs) - - for i in range (0, len(res)): - attr = res[i]["lDAPDisplayName"][0] - - if attr == "objectGUID": - attr = "nsUniqueId" - - index_config += read_and_sub_file(setup_path("fedorads-index.ldif"), - { "ATTR" : attr }) - - open(paths.fedoradsindex, 'w').write(index_config) - - setup_file(setup_path("fedorads-samba.ldif"), paths.fedoradssamba, - {"SAMBADN": names.sambadn, - "LDAPADMINPASS": ldapadminpass - }) - - mapping = "schema-map-fedora-ds-1.0" - backend_schema = "99_ad.ldif" - - # Build a schema file in Fedora DS format - backend_schema_data = schema.ldb.convert_schema_to_openldap("fedora-ds", open(setup_path(mapping), 'r').read()) - assert backend_schema_data is not None - open(os.path.join(paths.ldapdir, backend_schema), 'w').write(backend_schema_data) - - result.credentials.set_bind_dn(names.ldapmanagerdn) - - # Destory the target directory, or else setup-ds.pl will complain - fedora_ds_dir = os.path.join(paths.ldapdir, "slapd-samba4") - shutil.rmtree(fedora_ds_dir, True) - - result.slapd_provision_command = [slapd_path, "-D", fedora_ds_dir, "-i", paths.slapdpid]; - #In the 'provision' command line, stay in the foreground so we can easily kill it - result.slapd_provision_command.append("-d0") - - #the command for the final run is the normal script - result.slapd_command = [os.path.join(paths.ldapdir, "slapd-samba4", "start-slapd")] - - # If we were just looking for crashes up to this point, it's a - # good time to exit before we realise we don't have Fedora DS on - if ldap_dryrun_mode: - sys.exit(0) - - # Try to print helpful messages when the user has not specified the path to the setup-ds tool - if setup_ds_path is None: - raise ProvisioningError("Warning: Fedora DS LDAP-Backend must be setup with path to setup-ds, e.g. --setup-ds-path=\"/usr/sbin/setup-ds.pl\"!") - if not os.path.exists(setup_ds_path): - message (setup_ds_path) - raise ProvisioningError("Warning: Given Path to slapd does not exist!") - - # Run the Fedora DS setup utility - retcode = subprocess.call([setup_ds_path, "--silent", "--file", paths.fedoradsinf], close_fds=True, shell=False) - if retcode != 0: - raise ProvisioningError("setup-ds failed") - - # Load samba-admin - retcode = subprocess.call([ - os.path.join(paths.ldapdir, "slapd-samba4", "ldif2db"), "-s", names.sambadn, "-i", paths.fedoradssamba], - close_fds=True, shell=False) - if retcode != 0: - raise("ldib2db failed") - def create_phpldapadmin_config(path, setup_path, ldapi_uri): """Create a PHP LDAP admin configuration file. @@ -2024,12 +1483,12 @@ def create_phpldapadmin_config(path, setup_path, ldapi_uri): {"S4_LDAPI_URI": ldapi_uri}) -def create_zone_file(path, setup_path, dnsdomain, +def create_zone_file(message, paths, setup_path, dnsdomain, hostip, hostip6, hostname, realm, domainguid, ntdsguid): """Write out a DNS zone file, from the info in the current database. - :param path: Path of the new zone file. + :param paths: paths object :param setup_path: Setup path function. :param dnsdomain: DNS Domain name :param domaindn: DN of the Domain @@ -2056,7 +1515,22 @@ def create_zone_file(path, setup_path, dnsdomain, hostip_base_line = "" hostip_host_line = "" - setup_file(setup_path("provision.zone"), path, { + dns_dir = os.path.dirname(paths.dns) + + try: + shutil.rmtree(dns_dir, True) + except OSError: + pass + + os.mkdir(dns_dir, 0770) + + if paths.bind_gid is not None: + try: + os.chown(dns_dir, -1, paths.bind_gid) + except OSError: + message("Failed to chown %s to bind gid %u" % (dns_dir, paths.bind_gid)) + + setup_file(setup_path("provision.zone"), paths.dns, { "HOSTNAME": hostname, "DNSDOMAIN": dnsdomain, "REALM": realm, @@ -2071,12 +1545,12 @@ def create_zone_file(path, setup_path, dnsdomain, }) -def create_named_conf(path, setup_path, realm, dnsdomain, +def create_named_conf(paths, setup_path, realm, dnsdomain, private_dir): """Write out a file containing zone statements suitable for inclusion in a named.conf file (including GSS-TSIG configuration). - :param path: Path of the new named.conf file. + :param paths: all paths :param setup_path: Setup path function. :param realm: Realm name :param dnsdomain: DNS Domain name @@ -2084,11 +1558,12 @@ def create_named_conf(path, setup_path, realm, dnsdomain, :param keytab_name: File name of DNS keytab file """ - setup_file(setup_path("named.conf"), path, { + setup_file(setup_path("named.conf"), paths.namedconf, { "DNSDOMAIN": dnsdomain, "REALM": realm, + "ZONE_FILE": paths.dns, "REALM_WC": "*." + ".".join(realm.split(".")[1:]), - "PRIVATE_DIR": private_dir + "NAMED_CONF": paths.namedconf }) def create_named_txt(path, setup_path, realm, dnsdomain,