diff options
Diffstat (limited to 'source4/scripting/python/samba/provision')
-rw-r--r-- | source4/scripting/python/samba/provision/__init__.py | 2279 | ||||
-rw-r--r-- | source4/scripting/python/samba/provision/backend.py | 840 | ||||
-rw-r--r-- | source4/scripting/python/samba/provision/common.py | 82 | ||||
-rw-r--r-- | source4/scripting/python/samba/provision/descriptor.py | 359 | ||||
-rw-r--r-- | source4/scripting/python/samba/provision/sambadns.py | 1135 |
5 files changed, 0 insertions, 4695 deletions
diff --git a/source4/scripting/python/samba/provision/__init__.py b/source4/scripting/python/samba/provision/__init__.py deleted file mode 100644 index aac0ee36b2..0000000000 --- a/source4/scripting/python/samba/provision/__init__.py +++ /dev/null @@ -1,2279 +0,0 @@ -# Unix SMB/CIFS implementation. -# backend code for provisioning a Samba4 server - -# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2012 -# Copyright (C) Andrew Bartlett <abartlet@samba.org> 2008-2009 -# Copyright (C) Oliver Liebel <oliver@itc.li> 2008-2009 -# -# Based on the original in EJS: -# Copyright (C) Andrew Tridgell <tridge@samba.org> 2005 -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. -# - -"""Functions for setting up a Samba configuration.""" - -__docformat__ = "restructuredText" - -from base64 import b64encode -import os -import re -import pwd -import grp -import logging -import time -import uuid -import socket -import urllib -import string -import tempfile - -import ldb - -from samba.auth import system_session, admin_session -import samba -from samba.samba3 import smbd, passdb -from samba.samba3 import param as s3param -from samba.dsdb import DS_DOMAIN_FUNCTION_2000 -from samba import ( - Ldb, - MAX_NETBIOS_NAME_LEN, - check_all_substituted, - is_valid_netbios_char, - setup_file, - substitute_var, - valid_netbios_name, - version, - ) -from samba.dcerpc import security, misc -from samba.dcerpc.misc import ( - SEC_CHAN_BDC, - SEC_CHAN_WKSTA, - ) -from samba.dsdb import ( - DS_DOMAIN_FUNCTION_2003, - DS_DOMAIN_FUNCTION_2008_R2, - ENC_ALL_TYPES, - ) -from samba.idmap import IDmapDB -from samba.ms_display_specifiers import read_ms_ldif -from samba.ntacls import setntacl, getntacl, dsacl2fsacl -from samba.ndr import ndr_pack, ndr_unpack -from samba.provision.backend import ( - ExistingBackend, - FDSBackend, - LDBBackend, - OpenLDAPBackend, - ) -from samba.provision.descriptor import ( - get_empty_descriptor, - get_config_descriptor, - get_config_partitions_descriptor, - get_config_sites_descriptor, - get_config_ntds_quotas_descriptor, - get_config_delete_protected1_descriptor, - get_config_delete_protected1wd_descriptor, - get_config_delete_protected2_descriptor, - get_domain_descriptor, - get_domain_infrastructure_descriptor, - get_domain_builtin_descriptor, - get_domain_computers_descriptor, - get_domain_users_descriptor, - get_domain_controllers_descriptor, - get_domain_delete_protected1_descriptor, - get_domain_delete_protected2_descriptor, - get_dns_partition_descriptor, - get_dns_forest_microsoft_dns_descriptor, - get_dns_domain_microsoft_dns_descriptor, - ) -from samba.provision.common import ( - setup_path, - setup_add_ldif, - setup_modify_ldif, - ) -from samba.provision.sambadns import ( - get_dnsadmins_sid, - setup_ad_dns, - create_dns_update_list - ) - -import samba.param -import samba.registry -from samba.schema import Schema -from samba.samdb import SamDB -from samba.dbchecker import dbcheck - - -DEFAULT_POLICY_GUID = "31B2F340-016D-11D2-945F-00C04FB984F9" -DEFAULT_DC_POLICY_GUID = "6AC1786C-016F-11D2-945F-00C04fB984F9" -DEFAULTSITE = "Default-First-Site-Name" -LAST_PROVISION_USN_ATTRIBUTE = "lastProvisionUSN" - - -class ProvisionPaths(object): - - def __init__(self): - self.shareconf = None - self.hklm = None - self.hkcu = None - self.hkcr = None - self.hku = None - self.hkpd = None - self.hkpt = None - self.samdb = None - self.idmapdb = None - self.secrets = None - self.keytab = None - self.dns_keytab = None - self.dns = None - self.winsdb = None - self.private_dir = None - self.state_dir = None - - -class ProvisionNames(object): - - def __init__(self): - self.ncs = None - self.rootdn = None - self.domaindn = None - self.configdn = None - self.schemadn = None - self.dnsforestdn = None - self.dnsdomaindn = None - self.ldapmanagerdn = None - self.dnsdomain = None - self.realm = None - self.netbiosname = None - self.domain = None - self.hostname = None - self.sitename = None - self.smbconf = None - self.name_map = {} - - -def find_provision_key_parameters(samdb, secretsdb, idmapdb, paths, smbconf, - lp): - """Get key provision parameters (realm, domain, ...) from a given provision - - :param samdb: An LDB object connected to the sam.ldb file - :param secretsdb: An LDB object connected to the secrets.ldb file - :param idmapdb: An LDB object connected to the idmap.ldb file - :param paths: A list of path to provision object - :param smbconf: Path to the smb.conf file - :param lp: A LoadParm object - :return: A list of key provision parameters - """ - names = ProvisionNames() - names.adminpass = None - - # NT domain, kerberos realm, root dn, domain dn, domain dns name - names.domain = string.upper(lp.get("workgroup")) - names.realm = lp.get("realm") - names.dnsdomain = names.realm.lower() - basedn = samba.dn_from_dns_name(names.dnsdomain) - names.realm = string.upper(names.realm) - # netbiosname - # Get the netbiosname first (could be obtained from smb.conf in theory) - res = secretsdb.search(expression="(flatname=%s)" % - names.domain,base="CN=Primary Domains", - scope=ldb.SCOPE_SUBTREE, attrs=["sAMAccountName"]) - names.netbiosname = str(res[0]["sAMAccountName"]).replace("$","") - - names.smbconf = smbconf - - # That's a bit simplistic but it's ok as long as we have only 3 - # partitions - current = samdb.search(expression="(objectClass=*)", - base="", scope=ldb.SCOPE_BASE, - attrs=["defaultNamingContext", "schemaNamingContext", - "configurationNamingContext","rootDomainNamingContext", - "namingContexts"]) - - names.configdn = current[0]["configurationNamingContext"] - configdn = str(names.configdn) - names.schemadn = current[0]["schemaNamingContext"] - if not (ldb.Dn(samdb, basedn) == (ldb.Dn(samdb, - current[0]["defaultNamingContext"][0]))): - raise ProvisioningError(("basedn in %s (%s) and from %s (%s)" - "is not the same ..." % (paths.samdb, - str(current[0]["defaultNamingContext"][0]), - paths.smbconf, basedn))) - - names.domaindn=current[0]["defaultNamingContext"] - names.rootdn=current[0]["rootDomainNamingContext"] - names.ncs=current[0]["namingContexts"] - names.dnsforestdn = None - names.dnsdomaindn = None - - for i in range(0, len(names.ncs)): - nc = names.ncs[i] - - dnsforestdn = "DC=ForestDnsZones,%s" % (str(names.rootdn)) - if nc == dnsforestdn: - names.dnsforestdn = dnsforestdn - continue - - dnsdomaindn = "DC=DomainDnsZones,%s" % (str(names.domaindn)) - if nc == dnsdomaindn: - names.dnsdomaindn = dnsdomaindn - continue - - # default site name - res3 = samdb.search(expression="(objectClass=site)", - base="CN=Sites," + configdn, scope=ldb.SCOPE_ONELEVEL, attrs=["cn"]) - names.sitename = str(res3[0]["cn"]) - - # dns hostname and server dn - res4 = samdb.search(expression="(CN=%s)" % names.netbiosname, - base="OU=Domain Controllers,%s" % basedn, - scope=ldb.SCOPE_ONELEVEL, attrs=["dNSHostName"]) - names.hostname = str(res4[0]["dNSHostName"]).replace("." + names.dnsdomain, "") - - server_res = samdb.search(expression="serverReference=%s" % res4[0].dn, - attrs=[], base=configdn) - names.serverdn = server_res[0].dn - - # invocation id/objectguid - res5 = samdb.search(expression="(objectClass=*)", - base="CN=NTDS Settings,%s" % str(names.serverdn), - scope=ldb.SCOPE_BASE, - attrs=["invocationID", "objectGUID"]) - names.invocation = str(ndr_unpack(misc.GUID, res5[0]["invocationId"][0])) - names.ntdsguid = str(ndr_unpack(misc.GUID, res5[0]["objectGUID"][0])) - - # domain guid/sid - res6 = samdb.search(expression="(objectClass=*)", base=basedn, - scope=ldb.SCOPE_BASE, attrs=["objectGUID", - "objectSid","msDS-Behavior-Version" ]) - names.domainguid = str(ndr_unpack(misc.GUID, res6[0]["objectGUID"][0])) - names.domainsid = ndr_unpack( security.dom_sid, res6[0]["objectSid"][0]) - if res6[0].get("msDS-Behavior-Version") is None or \ - int(res6[0]["msDS-Behavior-Version"][0]) < DS_DOMAIN_FUNCTION_2000: - names.domainlevel = DS_DOMAIN_FUNCTION_2000 - else: - names.domainlevel = int(res6[0]["msDS-Behavior-Version"][0]) - - # policy guid - res7 = samdb.search(expression="(displayName=Default Domain Policy)", - base="CN=Policies,CN=System," + basedn, - scope=ldb.SCOPE_ONELEVEL, attrs=["cn","displayName"]) - names.policyid = str(res7[0]["cn"]).replace("{","").replace("}","") - # dc policy guid - res8 = samdb.search(expression="(displayName=Default Domain Controllers" - " Policy)", - base="CN=Policies,CN=System," + basedn, - scope=ldb.SCOPE_ONELEVEL, - attrs=["cn","displayName"]) - if len(res8) == 1: - names.policyid_dc = str(res8[0]["cn"]).replace("{","").replace("}","") - else: - names.policyid_dc = None - - res9 = idmapdb.search(expression="(cn=%s-%s)" % - (str(names.domainsid), security.DOMAIN_RID_ADMINISTRATOR), - attrs=["xidNumber", "type"]) - if len(res9) != 1: - raise ProvisioningError("Unable to find uid/gid for Domain Admins rid (%s-%s" % (str(names.domainsid), security.DOMAIN_RID_ADMINISTRATOR)) - if res9[0]["type"][0] == "ID_TYPE_BOTH": - names.root_gid = res9[0]["xidNumber"][0] - else: - names.root_gid = pwd.getpwuid(int(res9[0]["xidNumber"][0])).pw_gid - - res10 = samdb.search(expression="(samaccountname=dns)", - scope=ldb.SCOPE_SUBTREE, attrs=["dn"], - controls=["search_options:1:2"]) - if (len(res10) > 0): - has_legacy_dns_account = True - else: - has_legacy_dns_account = False - - res11 = samdb.search(expression="(samaccountname=dns-%s)" % names.netbiosname, - scope=ldb.SCOPE_SUBTREE, attrs=["dn"], - controls=["search_options:1:2"]) - if (len(res11) > 0): - has_dns_account = True - else: - has_dns_account = False - - if names.dnsdomaindn is not None: - if has_dns_account: - names.dns_backend = 'BIND9_DLZ' - else: - names.dns_backend = 'SAMBA_INTERNAL' - elif has_dns_account or has_legacy_dns_account: - names.dns_backend = 'BIND9_FLATFILE' - else: - names.dns_backend = 'NONE' - - dns_admins_sid = get_dnsadmins_sid(samdb, names.domaindn) - names.name_map['DnsAdmins'] = str(dns_admins_sid) - - return names - - -def update_provision_usn(samdb, low, high, id, replace=False): - """Update the field provisionUSN in sam.ldb - - This field is used to track range of USN modified by provision and - 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 id: The invocation id of the samba's dc - :param replace: A boolean indicating if the range should replace any - existing one or appended (default) - """ - - tab = [] - if not replace: - entry = samdb.search(base="@PROVISION", - scope=ldb.SCOPE_BASE, - attrs=[LAST_PROVISION_USN_ATTRIBUTE, "dn"]) - for e in entry[0][LAST_PROVISION_USN_ATTRIBUTE]: - if not re.search(';', e): - e = "%s;%s" % (e, id) - tab.append(str(e)) - - tab.append("%s-%s;%s" % (low, high, id)) - delta = ldb.Message() - delta.dn = ldb.Dn(samdb, "@PROVISION") - delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab, - ldb.FLAG_MOD_REPLACE, LAST_PROVISION_USN_ATTRIBUTE) - entry = samdb.search(expression='provisionnerID=*', - base="@PROVISION", scope=ldb.SCOPE_BASE, - attrs=["provisionnerID"]) - if len(entry) == 0 or len(entry[0]) == 0: - delta["provisionnerID"] = ldb.MessageElement(id, ldb.FLAG_MOD_ADD, "provisionnerID") - samdb.modify(delta) - - -def set_provision_usn(samdb, low, high, id): - """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 - :param id: The invocationId of the provision""" - - tab = [] - tab.append("%s-%s;%s" % (low, high, id)) - - delta = ldb.Message() - delta.dn = ldb.Dn(samdb, "@PROVISION") - delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab, - ldb.FLAG_MOD_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 USNs ranges modified by a provision or an upgradeprovision - - :param sam: An LDB object pointing to the sam.ldb - :return: a dictionary which keys are invocation id and values are an array - of integer representing the different ranges - """ - try: - entry = sam.search(expression="%s=*" % LAST_PROVISION_USN_ATTRIBUTE, - base="@PROVISION", scope=ldb.SCOPE_BASE, - attrs=[LAST_PROVISION_USN_ATTRIBUTE, "provisionnerID"]) - except ldb.LdbError, (ecode, emsg): - if ecode == ldb.ERR_NO_SUCH_OBJECT: - return None - raise - if len(entry) > 0: - myids = [] - range = {} - p = re.compile(r'-') - if entry[0].get("provisionnerID"): - for e in entry[0]["provisionnerID"]: - myids.append(str(e)) - for r in entry[0][LAST_PROVISION_USN_ATTRIBUTE]: - tab1 = str(r).split(';') - if len(tab1) == 2: - id = tab1[1] - else: - id = "default" - if (len(myids) > 0 and id not in myids): - continue - tab2 = p.split(tab1[0]) - if range.get(id) is None: - range[id] = [] - range[id].append(tab2[0]) - range[id].append(tab2[1]) - return range - else: - return None - - -class ProvisionResult(object): - """Result of a provision. - - :ivar server_role: The server role - :ivar paths: ProvisionPaths instance - :ivar domaindn: The domain dn, as string - """ - - def __init__(self): - self.server_role = None - self.paths = None - self.domaindn = None - self.lp = None - self.samdb = None - self.idmap = None - self.names = None - self.domainsid = None - self.adminpass_generated = None - self.adminpass = None - self.backend_result = None - - def report_logger(self, logger): - """Report this provision result to a logger.""" - logger.info( - "Once the above files are installed, your Samba4 server will " - "be ready to use") - if self.adminpass_generated: - logger.info("Admin password: %s", self.adminpass) - logger.info("Server Role: %s", self.server_role) - logger.info("Hostname: %s", self.names.hostname) - logger.info("NetBIOS Domain: %s", self.names.domain) - logger.info("DNS Domain: %s", self.names.dnsdomain) - logger.info("DOMAIN SID: %s", self.domainsid) - - if self.backend_result: - self.backend_result.report_logger(logger) - - -def check_install(lp, session_info, credentials): - """Check whether the current install seems ok. - - :param lp: Loadparm context - :param session_info: Session information - :param credentials: Credentials - """ - if lp.get("realm") == "": - raise Exception("Realm empty") - samdb = Ldb(lp.samdb_url(), session_info=session_info, - credentials=credentials, lp=lp) - if len(samdb.search("(cn=Administrator)")) != 1: - raise ProvisioningError("No administrator account found") - - -def findnss(nssfn, names): - """Find a user or group from a list of possibilities. - - :param nssfn: NSS Function to try (should raise KeyError if not found) - :param names: Names to check. - :return: Value return by first names list. - """ - for name in names: - try: - return nssfn(name) - except KeyError: - pass - raise KeyError("Unable to find user/group in %r" % names) - - -findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2] -findnss_gid = lambda names: findnss(grp.getgrnam, names)[2] - - -def provision_paths_from_lp(lp, dnsdomain): - """Set the default paths for provisioning. - - :param lp: Loadparm context. - :param dnsdomain: DNS Domain name - """ - paths = ProvisionPaths() - paths.private_dir = lp.get("private dir") - paths.state_dir = lp.get("state directory") - - # 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, "sam.ldb") - paths.idmapdb = os.path.join(paths.private_dir, "idmap.ldb") - paths.secrets = os.path.join(paths.private_dir, "secrets.ldb") - paths.privilege = os.path.join(paths.private_dir, "privilege.ldb") - paths.dns = os.path.join(paths.private_dir, "dns", dnsdomain + ".zone") - paths.dns_update_list = os.path.join(paths.private_dir, "dns_update_list") - paths.spn_update_list = os.path.join(paths.private_dir, "spn_update_list") - paths.namedconf = os.path.join(paths.private_dir, "named.conf") - paths.namedconf_update = os.path.join(paths.private_dir, "named.conf.update") - paths.namedtxt = os.path.join(paths.private_dir, "named.txt") - paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf") - paths.winsdb = os.path.join(paths.private_dir, "wins.ldb") - paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi") - paths.hklm = "hklm.ldb" - paths.hkcr = "hkcr.ldb" - paths.hkcu = "hkcu.ldb" - paths.hku = "hku.ldb" - paths.hkpd = "hkpd.ldb" - paths.hkpt = "hkpt.ldb" - paths.sysvol = lp.get("path", "sysvol") - paths.netlogon = lp.get("path", "netlogon") - paths.smbconf = lp.configfile - return paths - - -def determine_netbios_name(hostname): - """Determine a netbios name from a hostname.""" - # remove forbidden chars and force the length to be <16 - netbiosname = "".join([x for x in hostname if is_valid_netbios_char(x)]) - return netbiosname[:MAX_NETBIOS_NAME_LEN].upper() - - -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): - """Guess configuration settings to use.""" - - if hostname is None: - hostname = socket.gethostname().split(".")[0] - - netbiosname = lp.get("netbios name") - if netbiosname is None: - netbiosname = determine_netbios_name(hostname) - netbiosname = netbiosname.upper() - if not valid_netbios_name(netbiosname): - raise InvalidNetbiosName(netbiosname) - - if dnsdomain is None: - dnsdomain = lp.get("realm") - if dnsdomain is None or dnsdomain == "": - raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp.configfile) - - 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 %s!" % lp.configfile) - - serverrole = serverrole.lower() - - realm = dnsdomain.upper() - - if lp.get("realm") == "": - raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp.configfile) - - if lp.get("realm").upper() != realm: - raise ProvisioningError("guess_names: 'realm=%s' in %s must match chosen realm '%s'! Please remove the smb.conf file and let provision generate it" % (lp.get("realm").upper(), realm, lp.configfile)) - - if lp.get("server role").lower() != serverrole: - raise ProvisioningError("guess_names: 'server role=%s' in %s must match chosen server role '%s'! Please remove the smb.conf file and let provision generate it" % (lp.get("server role"), lp.configfile, serverrole)) - - if serverrole == "active directory domain controller": - if domain is None: - # This will, for better or worse, default to 'WORKGROUP' - domain = lp.get("workgroup") - domain = domain.upper() - - if lp.get("workgroup").upper() != domain: - 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 = samba.dn_from_dns_name(dnsdomain) - - if domain == netbiosname: - raise ProvisioningError("guess_names: Domain '%s' must not be equal to short host name '%s'!" % (domain, netbiosname)) - else: - domain = netbiosname - if domaindn is None: - domaindn = "DC=" + netbiosname - - if not valid_netbios_name(domain): - raise InvalidNetbiosName(domain) - - if hostname.upper() == realm: - raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm, hostname)) - if netbiosname.upper() == 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 - - if configdn is None: - configdn = "CN=Configuration," + rootdn - if schemadn is None: - schemadn = "CN=Schema," + configdn - - if sitename is None: - sitename = DEFAULTSITE - - names = ProvisionNames() - names.rootdn = rootdn - names.domaindn = domaindn - names.configdn = configdn - names.schemadn = schemadn - names.ldapmanagerdn = "CN=Manager," + rootdn - names.dnsdomain = dnsdomain - names.domain = domain - names.realm = realm - names.netbiosname = netbiosname - names.hostname = hostname - names.sitename = sitename - names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % ( - netbiosname, sitename, configdn) - - return names - - -def make_smbconf(smbconf, hostname, domain, realm, targetdir, - serverrole=None, eadb=False, use_ntvfs=False, lp=None, - global_param=None): - """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] - - netbiosname = determine_netbios_name(hostname) - - if serverrole is None: - serverrole = "standalone server" - - assert domain is not None - domain = domain.upper() - - assert realm is not None - realm = realm.upper() - - global_settings = { - "netbios name": netbiosname, - "workgroup": domain, - "realm": realm, - "server role": serverrole, - } - - if lp is None: - lp = samba.param.LoadParm() - #Load non-existent file - if os.path.exists(smbconf): - lp.load(smbconf) - - if global_param is not None: - for ent in global_param: - if global_param[ent] is not None: - global_settings[ent] = " ".join(global_param[ent]) - - if targetdir is not None: - global_settings["private dir"] = os.path.abspath(os.path.join(targetdir, "private")) - global_settings["lock dir"] = os.path.abspath(targetdir) - global_settings["state directory"] = os.path.abspath(os.path.join(targetdir, "state")) - global_settings["cache directory"] = os.path.abspath(os.path.join(targetdir, "cache")) - - lp.set("lock dir", os.path.abspath(targetdir)) - lp.set("state directory", global_settings["state directory"]) - lp.set("cache directory", global_settings["cache directory"]) - - if eadb: - if use_ntvfs and not lp.get("posix:eadb"): - if targetdir is not None: - privdir = os.path.join(targetdir, "private") - else: - privdir = lp.get("private dir") - lp.set("posix:eadb", os.path.abspath(os.path.join(privdir, "eadb.tdb"))) - elif not use_ntvfs and not lp.get("xattr_tdb:file"): - if targetdir is not None: - statedir = os.path.join(targetdir, "state") - else: - statedir = lp.get("state directory") - lp.set("xattr_tdb:file", os.path.abspath(os.path.join(statedir, "xattr.tdb"))) - - shares = {} - if serverrole == "active directory domain controller": - shares["sysvol"] = os.path.join(lp.get("state directory"), "sysvol") - shares["netlogon"] = os.path.join(shares["sysvol"], realm.lower(), - "scripts") - else: - global_settings["passdb backend"] = "samba_dsdb" - - f = open(smbconf, 'w') - try: - f.write("[globals]\n") - for key, val in global_settings.iteritems(): - f.write("\t%s = %s\n" % (key, val)) - f.write("\n") - - for name, path in shares.iteritems(): - f.write("[%s]\n" % name) - f.write("\tpath = %s\n" % path) - f.write("\tread only = no\n") - f.write("\n") - finally: - f.close() - # reload the smb.conf - lp.load(smbconf) - - # and dump it without any values that are the default - # this ensures that any smb.conf parameters that were set - # on the provision/join command line are set in the resulting smb.conf - f = open(smbconf, mode='w') - try: - lp.dump(f, False) - finally: - f.close() - - -def setup_name_mappings(idmap, sid, root_uid, nobody_uid, - users_gid, root_gid): - """setup reasonable name mappings for sam names to unix names. - - :param samdb: SamDB object. - :param idmap: IDmap db object. - :param sid: The domain sid. - :param domaindn: The domain DN. - :param root_uid: uid of the UNIX root user. - :param nobody_uid: uid of the UNIX nobody user. - :param users_gid: gid of the UNIX users group. - :param root_gid: gid of the UNIX root group. - """ - idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid) - - 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, logger, lp, session_info, - provision_backend, names, schema, serverrole, - erase=False): - """Setup the partitions for the SAM database. - - Alternatively, provision() may call this, and then populate the database. - - :note: This will wipe the Sam Database! - - :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 - - # We use options=["modules:"] to stop the modules loading - we - # just want to wipe and re-initialise the database, not start it up - - try: - os.unlink(samdb_path) - except OSError: - pass - - samdb = Ldb(url=samdb_path, session_info=session_info, - lp=lp, options=["modules:"]) - - ldap_backend_line = "# No LDAP backend" - if provision_backend.type != "ldb": - ldap_backend_line = "ldapBackend: %s" % provision_backend.ldap_uri - - samdb.transaction_start() - try: - logger.info("Setting up sam.ldb partitions and settings") - setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), { - "LDAP_BACKEND_LINE": ldap_backend_line - }) - - - setup_add_ldif(samdb, setup_path("provision_init.ldif"), { - "BACKEND_TYPE": provision_backend.type, - "SERVER_ROLE": serverrole - }) - - logger.info("Setting up sam.ldb rootDSE") - setup_samdb_rootdse(samdb, names) - except: - samdb.transaction_cancel() - raise - else: - samdb.transaction_commit() - - -def secretsdb_self_join(secretsdb, domain, - netbiosname, machinepass, domainsid=None, - realm=None, dnsdomain=None, - keytab_path=None, - key_version_number=1, - secure_channel_type=SEC_CHAN_WKSTA): - """Add domain join-specific bits to a secrets database. - - :param secretsdb: Ldb Handle to the secrets database - :param machinepass: Machine password - """ - attrs = ["whenChanged", - "secret", - "priorSecret", - "priorChanged", - "krb5Keytab", - "privateKeytab"] - - if realm is not None: - if dnsdomain is None: - 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" % (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)(!(distinguishedName=%s)))" % (domain, realm, str(domainsid), str(msg.dn))), - scope=ldb.SCOPE_ONELEVEL) - - for del_msg in res: - 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"][0]] - msg["priorWhenChanged"] = [res[0]["whenChanged"][0]] - - try: - msg["privateKeytab"] = [res[0]["privateKeytab"][0]] - except KeyError: - pass - - try: - msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]] - except KeyError: - pass - - for el in 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 setup_secretsdb(paths, session_info, backend_credentials, lp): - """Setup the secrets database. - - :note: This function does not handle exceptions and transaction on purpose, - it's up to the caller to do this job. - - :param path: Path to the secrets database. - :param session_info: Session info. - :param credentials: Credentials - :param lp: Loadparm context - :return: LDB handle for the created secrets database - """ - 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.load_ldif_file_add(setup_path("secrets_init.ldif")) - secrets_ldb = Ldb(path, session_info=session_info, lp=lp) - secrets_ldb.transaction_start() - 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()) - }) - except: - secrets_ldb.transaction_cancel() - raise - return secrets_ldb - - -def setup_privileges(path, session_info, lp): - """Setup the privileges database. - - :param path: Path to the privileges database. - :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, session_info, lp): - """Setup the registry. - - :param path: Path to the registry database - :param session_info: Session information - :param credentials: Credentials - :param lp: Loadparm context - """ - reg = samba.registry.Registry() - hive = samba.registry.open_ldb(path, session_info=session_info, lp_ctx=lp) - 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) - - -def setup_idmapdb(path, session_info, lp): - """Setup the idmap database. - - :param path: path to the idmap database - :param session_info: Session information - :param credentials: Credentials - :param lp: Loadparm context - """ - if os.path.exists(path): - os.unlink(path) - - idmap_ldb = IDmapDB(path, session_info=session_info, lp=lp) - idmap_ldb.erase() - idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif")) - return idmap_ldb - - -def setup_samdb_rootdse(samdb, names): - """Setup the SamDB rootdse. - - :param samdb: Sam Database handle - """ - setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), { - "SCHEMADN": names.schemadn, - "DOMAINDN": names.domaindn, - "ROOTDN" : names.rootdn, - "CONFIGDN": names.configdn, - "SERVERDN": names.serverdn, - }) - - -def setup_self_join(samdb, admin_session_info, names, fill, machinepass, - dns_backend, dnspass, domainsid, next_rid, invocationid, - policyguid, policyguid_dc, - domainControllerFunctionality, ntdsguid=None, dc_rid=None): - """Join a host to its own domain.""" - assert isinstance(invocationid, str) - if ntdsguid is not None: - ntdsguid_line = "objectGUID: %s\n"%ntdsguid - else: - ntdsguid_line = "" - - if dc_rid is None: - dc_rid = next_rid - - setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), { - "CONFIGDN": names.configdn, - "SCHEMADN": names.schemadn, - "DOMAINDN": names.domaindn, - "SERVERDN": names.serverdn, - "INVOCATIONID": invocationid, - "NETBIOSNAME": names.netbiosname, - "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain), - "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')), - "DOMAINSID": str(domainsid), - "DCRID": str(dc_rid), - "SAMBA_VERSION_STRING": version, - "NTDSGUID": ntdsguid_line, - "DOMAIN_CONTROLLER_FUNCTIONALITY": str( - domainControllerFunctionality), - "RIDALLOCATIONSTART": str(next_rid + 100), - "RIDALLOCATIONEND": str(next_rid + 100 + 499)}) - - setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), { - "POLICYGUID": policyguid, - "POLICYGUID_DC": policyguid_dc, - "DNSDOMAIN": names.dnsdomain, - "DOMAINDN": names.domaindn}) - - # If we are setting up a subdomain, then this has been replicated in, so we - # don't need to add it - if fill == FILL_FULL: - setup_add_ldif(samdb, setup_path("provision_self_join_config.ldif"), { - "CONFIGDN": names.configdn, - "SCHEMADN": names.schemadn, - "DOMAINDN": names.domaindn, - "SERVERDN": names.serverdn, - "INVOCATIONID": invocationid, - "NETBIOSNAME": names.netbiosname, - "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain), - "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')), - "DOMAINSID": str(domainsid), - "DCRID": str(dc_rid), - "SAMBA_VERSION_STRING": version, - "NTDSGUID": ntdsguid_line, - "DOMAIN_CONTROLLER_FUNCTIONALITY": str( - domainControllerFunctionality)}) - - # Setup fSMORoleOwner entries to point at the newly created DC entry - setup_modify_ldif(samdb, - setup_path("provision_self_join_modify_config.ldif"), { - "CONFIGDN": names.configdn, - "SCHEMADN": names.schemadn, - "DEFAULTSITE": names.sitename, - "NETBIOSNAME": names.netbiosname, - "SERVERDN": names.serverdn, - }) - - system_session_info = system_session() - samdb.set_session_info(system_session_info) - # Setup fSMORoleOwner entries to point at the newly created DC entry to - # modify a serverReference under cn=config when we are a subdomain, we must - # be system due to ACLs - setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), { - "DOMAINDN": names.domaindn, - "SERVERDN": names.serverdn, - "NETBIOSNAME": names.netbiosname, - }) - - samdb.set_session_info(admin_session_info) - - if dns_backend != "SAMBA_INTERNAL": - # This is Samba4 specific and should be replaced by the correct - # DNS AD-style setup - setup_add_ldif(samdb, setup_path("provision_dns_add_samba.ldif"), { - "DNSDOMAIN": names.dnsdomain, - "DOMAINDN": names.domaindn, - "DNSPASS_B64": b64encode(dnspass.encode('utf-16-le')), - "HOSTNAME" : names.hostname, - "DNSNAME" : '%s.%s' % ( - 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 create_gpo_struct(policy_path): - if not os.path.exists(policy_path): - os.makedirs(policy_path, 0775) - f = open(os.path.join(policy_path, "GPT.INI"), 'w') - try: - f.write("[General]\r\nVersion=0") - finally: - f.close() - 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, session_info, provision_backend, lp, names, - logger, fill, serverrole, schema, am_rodc=False): - """Setup a complete SAM Database. - - :note: This will wipe the main SAM database file! - """ - - # Also wipes the database - setup_samdb_partitions(path, logger=logger, lp=lp, - provision_backend=provision_backend, session_info=session_info, - names=names, serverrole=serverrole, schema=schema) - - # 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) - - logger.info("Pre-loading the Samba 4 and AD schema") - - # Load the schema from the one we computed earlier - samdb.set_schema(schema, write_indices_and_attributes=False) - - # 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) - - # But we have to give it one more kick to have it use the schema - # during provision - it needs, now that it is connected, to write - # the schema @ATTRIBUTES and @INDEXLIST records to the database. - samdb.set_schema(schema, write_indices_and_attributes=True) - - return samdb - - -def fill_samdb(samdb, lp, names, logger, domainsid, domainguid, policyguid, - policyguid_dc, fill, adminpass, krbtgtpass, machinepass, dns_backend, - dnspass, invocationid, ntdsguid, serverrole, am_rodc=False, - dom_for_fun_level=None, schema=None, next_rid=None, dc_rid=None): - - if next_rid is None: - next_rid = 1000 - - # Provision does not make much sense values larger than 1000000000 - # as the upper range of the rIDAvailablePool is 1073741823 and - # we don't want to create a domain that cannot allocate rids. - if next_rid < 1000 or next_rid > 1000000000: - error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid) - error += "the valid range is %u-%u. The default is %u." % ( - 1000, 1000000000, 1000) - raise ProvisioningError(error) - - # ATTENTION: Do NOT change these default values without discussion with the - # team and/or release manager. They have a big impact on the whole program! - domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2 - - if dom_for_fun_level is None: - dom_for_fun_level = DS_DOMAIN_FUNCTION_2003 - - if dom_for_fun_level > domainControllerFunctionality: - raise ProvisioningError("You want to run SAMBA 4 on a domain and forest function level which itself is higher than its actual DC function level (2008_R2). This won't work!") - - domainFunctionality = dom_for_fun_level - forestFunctionality = dom_for_fun_level - - # Set the NTDS settings DN manually - in order to have it already around - # before the provisioned tree exists and we connect - samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn) - - samdb.transaction_start() - try: - # 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. - - # These will be fixed into the database via the database - # modifictions below, but we need them set from the start. - samdb.set_opaque_integer("domainFunctionality", domainFunctionality) - samdb.set_opaque_integer("forestFunctionality", forestFunctionality) - samdb.set_opaque_integer("domainControllerFunctionality", - domainControllerFunctionality) - - samdb.set_domain_sid(str(domainsid)) - samdb.set_invocation_id(invocationid) - - logger.info("Adding DomainDN: %s" % names.domaindn) - - # impersonate domain admin - admin_session_info = admin_session(lp, str(domainsid)) - samdb.set_session_info(admin_session_info) - if domainguid is not None: - 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, - "DOMAINSID": str(domainsid), - "DESCRIPTOR": descr, - "DOMAINGUID": domainguid_line - }) - - setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), { - "DOMAINDN": names.domaindn, - "CREATTIME": str(samba.unix2nttime(int(time.time()))), - "NEXTRID": str(next_rid), - "DEFAULTSITE": names.sitename, - "CONFIGDN": names.configdn, - "POLICYGUID": policyguid, - "DOMAIN_FUNCTIONALITY": str(domainFunctionality), - "SAMBA_VERSION_STRING": version - }) - - # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it - if fill == FILL_FULL: - logger.info("Adding configuration container") - descr = b64encode(get_config_descriptor(domainsid)) - setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), { - "CONFIGDN": names.configdn, - "DESCRIPTOR": descr, - }) - - # The LDIF here was created when the Schema object was constructed - logger.info("Setting up sam.ldb schema") - samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"]) - samdb.modify_ldif(schema.schema_dn_modify) - samdb.write_prefixes_from_schema() - samdb.add_ldif(schema.schema_data, controls=["relax:0"]) - setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"), - {"SCHEMADN": names.schemadn}) - - # Now register this container in the root of the forest - msg = ldb.Message(ldb.Dn(samdb, names.domaindn)) - msg["subRefs"] = ldb.MessageElement(names.configdn , ldb.FLAG_MOD_ADD, - "subRefs") - - except: - samdb.transaction_cancel() - raise - else: - samdb.transaction_commit() - - samdb.transaction_start() - try: - samdb.invocation_id = invocationid - - # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it - if fill == FILL_FULL: - logger.info("Setting up sam.ldb configuration data") - - partitions_descr = b64encode(get_config_partitions_descriptor(domainsid)) - sites_descr = b64encode(get_config_sites_descriptor(domainsid)) - ntdsquotas_descr = b64encode(get_config_ntds_quotas_descriptor(domainsid)) - protected1_descr = b64encode(get_config_delete_protected1_descriptor(domainsid)) - protected1wd_descr = b64encode(get_config_delete_protected1wd_descriptor(domainsid)) - protected2_descr = b64encode(get_config_delete_protected2_descriptor(domainsid)) - - setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), { - "CONFIGDN": names.configdn, - "NETBIOSNAME": names.netbiosname, - "DEFAULTSITE": names.sitename, - "DNSDOMAIN": names.dnsdomain, - "DOMAIN": names.domain, - "SCHEMADN": names.schemadn, - "DOMAINDN": names.domaindn, - "SERVERDN": names.serverdn, - "FOREST_FUNCTIONALITY": str(forestFunctionality), - "DOMAIN_FUNCTIONALITY": str(domainFunctionality), - "NTDSQUOTAS_DESCRIPTOR": ntdsquotas_descr, - "LOSTANDFOUND_DESCRIPTOR": protected1wd_descr, - "SERVICES_DESCRIPTOR": protected1_descr, - "PHYSICALLOCATIONS_DESCRIPTOR": protected1wd_descr, - "FORESTUPDATES_DESCRIPTOR": protected1wd_descr, - "EXTENDEDRIGHTS_DESCRIPTOR": protected2_descr, - "PARTITIONS_DESCRIPTOR": partitions_descr, - "SITES_DESCRIPTOR": sites_descr, - }) - - logger.info("Setting up display specifiers") - display_specifiers_ldif = read_ms_ldif( - setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt')) - display_specifiers_ldif = substitute_var(display_specifiers_ldif, - {"CONFIGDN": names.configdn}) - check_all_substituted(display_specifiers_ldif) - samdb.add_ldif(display_specifiers_ldif) - - logger.info("Modifying display specifiers") - setup_modify_ldif(samdb, - setup_path("provision_configuration_modify.ldif"), { - "CONFIGDN": names.configdn, - "DISPLAYSPECIFIERS_DESCRIPTOR": protected2_descr - }) - - logger.info("Adding users container") - users_desc = b64encode(get_domain_users_descriptor(domainsid)) - setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), { - "DOMAINDN": names.domaindn, - "USERS_DESCRIPTOR": users_desc - }) - logger.info("Modifying users container") - setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), { - "DOMAINDN": names.domaindn}) - logger.info("Adding computers container") - computers_desc = b64encode(get_domain_computers_descriptor(domainsid)) - setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), { - "DOMAINDN": names.domaindn, - "COMPUTERS_DESCRIPTOR": computers_desc - }) - logger.info("Modifying computers container") - setup_modify_ldif(samdb, - setup_path("provision_computers_modify.ldif"), { - "DOMAINDN": names.domaindn}) - logger.info("Setting up sam.ldb data") - infrastructure_desc = b64encode(get_domain_infrastructure_descriptor(domainsid)) - lostandfound_desc = b64encode(get_domain_delete_protected2_descriptor(domainsid)) - system_desc = b64encode(get_domain_delete_protected1_descriptor(domainsid)) - builtin_desc = b64encode(get_domain_builtin_descriptor(domainsid)) - controllers_desc = b64encode(get_domain_controllers_descriptor(domainsid)) - setup_add_ldif(samdb, setup_path("provision.ldif"), { - "CREATTIME": str(samba.unix2nttime(int(time.time()))), - "DOMAINDN": names.domaindn, - "NETBIOSNAME": names.netbiosname, - "DEFAULTSITE": names.sitename, - "CONFIGDN": names.configdn, - "SERVERDN": names.serverdn, - "RIDAVAILABLESTART": str(next_rid + 600), - "POLICYGUID_DC": policyguid_dc, - "INFRASTRUCTURE_DESCRIPTOR": infrastructure_desc, - "LOSTANDFOUND_DESCRIPTOR": lostandfound_desc, - "SYSTEM_DESCRIPTOR": system_desc, - "BUILTIN_DESCRIPTOR": builtin_desc, - "DOMAIN_CONTROLLERS_DESCRIPTOR": controllers_desc, - }) - - # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it - if fill == FILL_FULL: - setup_modify_ldif(samdb, - setup_path("provision_configuration_references.ldif"), { - "CONFIGDN": names.configdn, - "SCHEMADN": names.schemadn}) - - logger.info("Setting up well known security principals") - protected1wd_descr = b64encode(get_config_delete_protected1wd_descriptor(domainsid)) - setup_add_ldif(samdb, setup_path("provision_well_known_sec_princ.ldif"), { - "CONFIGDN": names.configdn, - "WELLKNOWNPRINCIPALS_DESCRIPTOR": protected1wd_descr, - }) - - if fill == FILL_FULL or fill == FILL_SUBDOMAIN: - setup_modify_ldif(samdb, - setup_path("provision_basedn_references.ldif"), - {"DOMAINDN": names.domaindn}) - - logger.info("Setting up sam.ldb users and groups") - setup_add_ldif(samdb, setup_path("provision_users.ldif"), { - "DOMAINDN": names.domaindn, - "DOMAINSID": str(domainsid), - "ADMINPASS_B64": b64encode(adminpass.encode('utf-16-le')), - "KRBTGTPASS_B64": b64encode(krbtgtpass.encode('utf-16-le')) - }) - - logger.info("Setting up self join") - setup_self_join(samdb, admin_session_info, names=names, fill=fill, - invocationid=invocationid, - dns_backend=dns_backend, - dnspass=dnspass, - machinepass=machinepass, - domainsid=domainsid, - next_rid=next_rid, - dc_rid=dc_rid, - policyguid=policyguid, - policyguid_dc=policyguid_dc, - domainControllerFunctionality=domainControllerFunctionality, - ntdsguid=ntdsguid) - - ntds_dn = "CN=NTDS Settings,%s" % names.serverdn - names.ntdsguid = samdb.searchone(basedn=ntds_dn, - attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE) - assert isinstance(names.ntdsguid, str) - except: - samdb.transaction_cancel() - raise - else: - samdb.transaction_commit() - return samdb - - -FILL_FULL = "FULL" -FILL_SUBDOMAIN = "SUBDOMAIN" -FILL_NT4SYNC = "NT4SYNC" -FILL_DRS = "DRS" -SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)" -POLICIES_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)(A;OICI;0x001301bf;;;PA)" -SYSVOL_SERVICE="sysvol" - -def set_dir_acl(path, acl, lp, domsid, use_ntvfs, passdb, service=SYSVOL_SERVICE): - setntacl(lp, path, acl, domsid, use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service) - for root, dirs, files in os.walk(path, topdown=False): - for name in files: - setntacl(lp, os.path.join(root, name), acl, domsid, - use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service) - for name in dirs: - setntacl(lp, os.path.join(root, name), acl, domsid, - use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service) - - -def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb): - """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), - use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=SYSVOL_SERVICE) - - res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn), - attrs=["cn", "nTSecurityDescriptor"], - expression="", scope=ldb.SCOPE_ONELEVEL) - - 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, domainsid), lp, - str(domainsid), use_ntvfs, - passdb=passdb) - - -def setsysvolacl(samdb, netlogon, sysvol, uid, gid, domainsid, dnsdomain, - domaindn, lp, use_ntvfs): - """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 uid: The UID of the "Administrator" user - :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=...) - """ - s4_passdb = None - - if not use_ntvfs: - # This will ensure that the smbd code we are running when setting ACLs - # is initialised with the smb.conf - s3conf = s3param.get_context() - s3conf.load(lp.configfile) - # ensure we are using the right samba_dsdb passdb backend, no matter what - s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url) - passdb.reload_static_pdb() - - # ensure that we init the samba_dsdb backend, so the domain sid is - # marked in secrets.tdb - s4_passdb = passdb.PDB(s3conf.get("passdb backend")) - - # now ensure everything matches correctly, to avoid wierd issues - if passdb.get_global_sam_sid() != domainsid: - raise ProvisioningError('SID as seen by smbd [%s] does not match SID as seen by the provision script [%s]!' % (passdb.get_global_sam_sid(), domainsid)) - - domain_info = s4_passdb.domain_info() - if domain_info["dom_sid"] != domainsid: - raise ProvisioningError('SID as seen by pdb_samba_dsdb [%s] does not match SID as seen by the provision script [%s]!' % (domain_info["dom_sid"], domainsid)) - - if domain_info["dns_domain"].upper() != dnsdomain.upper(): - raise ProvisioningError('Realm as seen by pdb_samba_dsdb [%s] does not match Realm as seen by the provision script [%s]!' % (domain_info["dns_domain"].upper(), dnsdomain.upper())) - - - try: - if use_ntvfs: - os.chown(sysvol, -1, gid) - except OSError: - canchown = False - else: - canchown = True - - # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level) - setntacl(lp,sysvol, SYSVOL_ACL, str(domainsid), use_ntvfs=use_ntvfs, - skip_invalid_chown=True, passdb=s4_passdb, - service=SYSVOL_SERVICE) - for root, dirs, files in os.walk(sysvol, topdown=False): - for name in files: - if use_ntvfs and canchown: - os.chown(os.path.join(root, name), -1, gid) - setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid), - use_ntvfs=use_ntvfs, skip_invalid_chown=True, - passdb=s4_passdb, service=SYSVOL_SERVICE) - for name in dirs: - if use_ntvfs and canchown: - os.chown(os.path.join(root, name), -1, gid) - setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid), - use_ntvfs=use_ntvfs, skip_invalid_chown=True, - passdb=s4_passdb, service=SYSVOL_SERVICE) - - # Set acls on Policy folder and policies folders - set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb=s4_passdb) - -def acl_type(direct_db_access): - if direct_db_access: - return "DB" - else: - return "VFS" - -def check_dir_acl(path, acl, lp, domainsid, direct_db_access): - fsacl = getntacl(lp, path, direct_db_access=direct_db_access, service=SYSVOL_SERVICE) - fsacl_sddl = fsacl.as_sddl(domainsid) - if fsacl_sddl != acl: - raise ProvisioningError('%s ACL on GPO directory %s %s does not match expected value %s from GPO object' % (acl_type(direct_db_access), path, fsacl_sddl, acl)) - - for root, dirs, files in os.walk(path, topdown=False): - for name in files: - fsacl = getntacl(lp, os.path.join(root, name), - direct_db_access=direct_db_access, service=SYSVOL_SERVICE) - if fsacl is None: - raise ProvisioningError('%s ACL on GPO file %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name))) - fsacl_sddl = fsacl.as_sddl(domainsid) - if fsacl_sddl != acl: - raise ProvisioningError('%s ACL on GPO file %s %s does not match expected value %s from GPO object' % (acl_type(direct_db_access), os.path.join(root, name), fsacl_sddl, acl)) - - for name in dirs: - fsacl = getntacl(lp, os.path.join(root, name), - direct_db_access=direct_db_access, service=SYSVOL_SERVICE) - if fsacl is None: - raise ProvisioningError('%s ACL on GPO directory %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name))) - fsacl_sddl = fsacl.as_sddl(domainsid) - if fsacl_sddl != acl: - raise ProvisioningError('%s ACL on GPO directory %s %s does not match expected value %s from GPO object' % (acl_type(direct_db_access), os.path.join(root, name), fsacl_sddl, acl)) - - -def check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, - direct_db_access): - """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") - fsacl = getntacl(lp, root_policy_path, - direct_db_access=direct_db_access, service=SYSVOL_SERVICE) - if fsacl is None: - raise ProvisioningError('DB ACL on policy root %s %s not found!' % (acl_type(direct_db_access), root_policy_path)) - fsacl_sddl = fsacl.as_sddl(domainsid) - if fsacl_sddl != POLICIES_ACL: - raise ProvisioningError('%s ACL on policy root %s %s does not match expected value %s from provision' % (acl_type(direct_db_access), root_policy_path, fsacl_sddl, fsacl)) - res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn), - attrs=["cn", "nTSecurityDescriptor"], - expression="", scope=ldb.SCOPE_ONELEVEL) - - for policy in res: - acl = ndr_unpack(security.descriptor, - str(policy["nTSecurityDescriptor"])).as_sddl() - policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"])) - check_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp, - domainsid, direct_db_access) - - -def checksysvolacl(samdb, netlogon, sysvol, domainsid, dnsdomain, domaindn, - 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 uid: The UID of the "Administrator" user - :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=...) - """ - - # This will ensure that the smbd code we are running when setting ACLs is initialised with the smb.conf - s3conf = s3param.get_context() - s3conf.load(lp.configfile) - # ensure we are using the right samba_dsdb passdb backend, no matter what - s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url) - # ensure that we init the samba_dsdb backend, so the domain sid is marked in secrets.tdb - s4_passdb = passdb.PDB(s3conf.get("passdb backend")) - - # now ensure everything matches correctly, to avoid wierd issues - if passdb.get_global_sam_sid() != domainsid: - raise ProvisioningError('SID as seen by smbd [%s] does not match SID as seen by the provision script [%s]!' % (passdb.get_global_sam_sid(), domainsid)) - - domain_info = s4_passdb.domain_info() - if domain_info["dom_sid"] != domainsid: - raise ProvisioningError('SID as seen by pdb_samba_dsdb [%s] does not match SID as seen by the provision script [%s]!' % (domain_info["dom_sid"], domainsid)) - - if domain_info["dns_domain"].upper() != dnsdomain.upper(): - raise ProvisioningError('Realm as seen by pdb_samba_dsdb [%s] does not match Realm as seen by the provision script [%s]!' % (domain_info["dns_domain"].upper(), dnsdomain.upper())) - - # Ensure we can read this directly, and via the smbd VFS - for direct_db_access in [True, False]: - # Check the SYSVOL_ACL on the sysvol folder and subfolder (first level) - for dir_path in [os.path.join(sysvol, dnsdomain), netlogon]: - fsacl = getntacl(lp, dir_path, direct_db_access=direct_db_access, service=SYSVOL_SERVICE) - if fsacl is None: - raise ProvisioningError('%s ACL on sysvol directory %s not found!' % (acl_type(direct_db_access), dir_path)) - fsacl_sddl = fsacl.as_sddl(domainsid) - if fsacl_sddl != SYSVOL_ACL: - raise ProvisioningError('%s ACL on sysvol directory %s %s does not match expected value %s from provision' % (acl_type(direct_db_access), dir_path, fsacl_sddl, SYSVOL_ACL)) - - # Check acls on Policy folder and policies folders - check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, - direct_db_access) - - -def interface_ips_v4(lp): - """return only IPv4 IPs""" - ips = samba.interface_ips(lp, False) - ret = [] - for i in ips: - if i.find(':') == -1: - ret.append(i) - return ret - - -def interface_ips_v6(lp, linklocal=False): - """return only IPv6 IPs""" - ips = samba.interface_ips(lp, False) - ret = [] - for i in ips: - if i.find(':') != -1 and (linklocal or i.find('%') == -1): - ret.append(i) - return ret - - -def provision_fill(samdb, secrets_ldb, logger, names, paths, - domainsid, schema=None, - targetdir=None, samdb_fill=FILL_FULL, - hostip=None, hostip6=None, - next_rid=1000, dc_rid=None, adminpass=None, krbtgtpass=None, - domainguid=None, policyguid=None, policyguid_dc=None, - invocationid=None, machinepass=None, ntdsguid=None, - dns_backend=None, dnspass=None, - serverrole=None, dom_for_fun_level=None, - am_rodc=False, lp=None, use_ntvfs=False, skip_sysvolacl=False): - # 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 = DEFAULT_POLICY_GUID - policyguid = policyguid.upper() - if policyguid_dc is None: - policyguid_dc = DEFAULT_DC_POLICY_GUID - policyguid_dc = policyguid_dc.upper() - - if invocationid is None: - invocationid = str(uuid.uuid4()) - - if krbtgtpass is None: - krbtgtpass = samba.generate_random_password(128, 255) - if machinepass is None: - machinepass = samba.generate_random_password(128, 255) - if dnspass is None: - dnspass = samba.generate_random_password(128, 255) - - samdb = fill_samdb(samdb, lp, names, logger=logger, - domainsid=domainsid, schema=schema, domainguid=domainguid, - policyguid=policyguid, policyguid_dc=policyguid_dc, - fill=samdb_fill, adminpass=adminpass, krbtgtpass=krbtgtpass, - invocationid=invocationid, machinepass=machinepass, - dns_backend=dns_backend, dnspass=dnspass, - ntdsguid=ntdsguid, serverrole=serverrole, - dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc, - next_rid=next_rid, dc_rid=dc_rid) - - if serverrole == "active directory domain controller": - - # Set up group policies (domain policy and domain controller - # policy) - create_default_gpo(paths.sysvol, names.dnsdomain, policyguid, - policyguid_dc) - if not skip_sysvolacl: - setsysvolacl(samdb, paths.netlogon, paths.sysvol, paths.root_uid, - paths.root_gid, domainsid, names.dnsdomain, - names.domaindn, lp, use_ntvfs) - else: - logger.info("Setting acl on sysvol skipped") - - secretsdb_self_join(secrets_ldb, domain=names.domain, - realm=names.realm, dnsdomain=names.dnsdomain, - netbiosname=names.netbiosname, domainsid=domainsid, - machinepass=machinepass, secure_channel_type=SEC_CHAN_BDC) - - # Now set up the right msDS-SupportedEncryptionTypes into the DB - # In future, this might be determined from some configuration - kerberos_enctypes = str(ENC_ALL_TYPES) - - try: - msg = ldb.Message(ldb.Dn(samdb, - samdb.searchone("distinguishedName", - expression="samAccountName=%s$" % names.netbiosname, - scope=ldb.SCOPE_SUBTREE))) - msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement( - elements=kerberos_enctypes, flags=ldb.FLAG_MOD_REPLACE, - name="msDS-SupportedEncryptionTypes") - samdb.modify(msg) - except ldb.LdbError, (enum, estr): - if enum != ldb.ERR_NO_SUCH_ATTRIBUTE: - # It might be that this attribute does not exist in this schema - raise - - setup_ad_dns(samdb, secrets_ldb, domainsid, names, paths, lp, logger, - hostip=hostip, hostip6=hostip6, dns_backend=dns_backend, - dnspass=dnspass, os_level=dom_for_fun_level, - targetdir=targetdir, site=DEFAULTSITE) - - domainguid = samdb.searchone(basedn=samdb.get_default_basedn(), - attribute="objectGUID") - assert isinstance(domainguid, str) - - lastProvisionUSNs = get_last_provision_usn(samdb) - maxUSN = get_max_usn(samdb, str(names.rootdn)) - if lastProvisionUSNs is not None: - update_provision_usn(samdb, 0, maxUSN, invocationid, 1) - else: - set_provision_usn(samdb, 0, maxUSN, invocationid) - - logger.info("Setting up sam.ldb rootDSE marking as synchronized") - setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"), - { 'NTDSGUID' : names.ntdsguid }) - - # fix any dangling GUIDs from the provision - logger.info("Fixing provision GUIDs") - chk = dbcheck(samdb, samdb_schema=samdb, verbose=False, fix=True, yes=True, - quiet=True) - samdb.transaction_start() - try: - # a small number of GUIDs are missing because of ordering issues in the - # provision code - for schema_obj in ['CN=Domain', 'CN=Organizational-Person', 'CN=Contact', 'CN=inetOrgPerson']: - chk.check_database(DN="%s,%s" % (schema_obj, names.schemadn), - scope=ldb.SCOPE_BASE, - attrs=['defaultObjectCategory']) - chk.check_database(DN="CN=IP Security,CN=System,%s" % names.domaindn, - scope=ldb.SCOPE_ONELEVEL, - attrs=['ipsecOwnersReference', - 'ipsecFilterReference', - 'ipsecISAKMPReference', - 'ipsecNegotiationPolicyReference', - 'ipsecNFAReference']) - except: - samdb.transaction_cancel() - raise - else: - samdb.transaction_commit() - - -_ROLES_MAP = { - "ROLE_STANDALONE": "standalone server", - "ROLE_DOMAIN_MEMBER": "member server", - "ROLE_DOMAIN_BDC": "active directory domain controller", - "ROLE_DOMAIN_PDC": "active directory domain controller", - "dc": "active directory domain controller", - "member": "member server", - "domain controller": "active directory domain controller", - "active directory domain controller": "active directory domain controller", - "member server": "member server", - "standalone": "standalone server", - "standalone server": "standalone server", - } - - -def sanitize_server_role(role): - """Sanitize a server role name. - - :param role: Server role - :raise ValueError: If the role can not be interpreted - :return: Sanitized server role (one of "member server", - "active directory domain controller", "standalone server") - """ - try: - return _ROLES_MAP[role] - except KeyError: - raise ValueError(role) - - -def provision_fake_ypserver(logger, samdb, domaindn, netbiosname, nisdomain, - maxuid, maxgid): - """Create AD entries for the fake ypserver. - - This is needed for being able to manipulate posix attrs via ADUC. - """ - samdb.transaction_start() - try: - logger.info("Setting up fake yp server settings") - setup_add_ldif(samdb, setup_path("ypServ30.ldif"), { - "DOMAINDN": domaindn, - "NETBIOSNAME": netbiosname, - "NISDOMAIN": nisdomain, - }) - except: - samdb.transaction_cancel() - raise - else: - samdb.transaction_commit() - - -def provision(logger, session_info, credentials, smbconf=None, - targetdir=None, samdb_fill=FILL_FULL, realm=None, rootdn=None, - domaindn=None, schemadn=None, configdn=None, serverdn=None, - domain=None, hostname=None, hostip=None, hostip6=None, domainsid=None, - next_rid=1000, dc_rid=None, adminpass=None, ldapadminpass=None, - krbtgtpass=None, domainguid=None, policyguid=None, policyguid_dc=None, - dns_backend=None, dns_forwarder=None, dnspass=None, - invocationid=None, machinepass=None, ntdsguid=None, - root=None, nobody=None, users=None, backup=None, aci=None, - serverrole=None, dom_for_fun_level=None, backend_type=None, - sitename=None, ol_mmr_urls=None, ol_olc=None, slapd_path="/bin/false", - useeadb=False, am_rodc=False, lp=None, use_ntvfs=False, - use_rfc2307=False, maxuid=None, maxgid=None, skip_sysvolacl=True): - """Provision samba4 - - :note: caution, this wipes all existing data! - """ - - try: - serverrole = sanitize_server_role(serverrole) - except ValueError: - raise ProvisioningError('server role (%s) should be one of "active directory domain controller", "member server", "standalone server"' % serverrole) - - if ldapadminpass is None: - # Make a new, random password between Samba and it's LDAP server - ldapadminpass = samba.generate_random_password(128, 255) - - if backend_type is None: - backend_type = "ldb" - - if domainsid is None: - domainsid = security.random_sid() - else: - domainsid = security.dom_sid(domainsid) - - root_uid = findnss_uid([root or "root"]) - nobody_uid = findnss_uid([nobody or "nobody"]) - users_gid = findnss_gid([users or "users", 'users', 'other', 'staff']) - root_gid = pwd.getpwuid(root_uid).pw_gid - - try: - bind_gid = findnss_gid(["bind", "named"]) - except KeyError: - bind_gid = None - - if targetdir is not None: - smbconf = os.path.join(targetdir, "etc", "smb.conf") - elif smbconf is None: - smbconf = samba.param.default_path() - if not os.path.exists(os.path.dirname(smbconf)): - os.makedirs(os.path.dirname(smbconf)) - - server_services = [] - global_param = {} - if use_rfc2307: - global_param["idmap_ldb:use rfc2307"] = ["yes"] - - if dns_backend != "SAMBA_INTERNAL": - server_services.append("-dns") - else: - if dns_forwarder is not None: - global_param["dns forwarder"] = [dns_forwarder] - - if use_ntvfs: - server_services.append("+smb") - server_services.append("-s3fs") - global_param["dcerpc endpoint servers"] = ["+winreg", "+srvsvc"] - - if len(server_services) > 0: - global_param["server services"] = server_services - - # only install a new smb.conf if there isn't one there already - 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 - f = open(smbconf, 'r') - try: - data = f.read().lstrip() - finally: - f.close() - if data is None or data == "": - make_smbconf(smbconf, hostname, domain, realm, - targetdir, serverrole=serverrole, - eadb=useeadb, use_ntvfs=use_ntvfs, - lp=lp, global_param=global_param) - else: - make_smbconf(smbconf, hostname, domain, realm, targetdir, - serverrole=serverrole, - eadb=useeadb, use_ntvfs=use_ntvfs, lp=lp, global_param=global_param) - - if lp is None: - lp = samba.param.LoadParm() - lp.load(smbconf) - names = guess_names(lp=lp, hostname=hostname, domain=domain, - dnsdomain=realm, serverrole=serverrole, domaindn=domaindn, - configdn=configdn, schemadn=schemadn, serverdn=serverdn, - sitename=sitename, rootdn=rootdn) - paths = provision_paths_from_lp(lp, names.dnsdomain) - - paths.bind_gid = bind_gid - paths.root_uid = root_uid; - paths.root_gid = root_gid - - if hostip is None: - logger.info("Looking up IPv4 addresses") - hostips = interface_ips_v4(lp) - if len(hostips) > 0: - hostip = hostips[0] - if len(hostips) > 1: - logger.warning("More than one IPv4 address found. Using %s", - hostip) - if hostip == "127.0.0.1": - hostip = None - if hostip is None: - logger.warning("No IPv4 address will be assigned") - - if hostip6 is None: - logger.info("Looking up IPv6 addresses") - hostips = interface_ips_v6(lp, linklocal=False) - if hostips: - hostip6 = hostips[0] - if len(hostips) > 1: - logger.warning("More than one IPv6 address found. Using %s", hostip6) - if hostip6 is None: - logger.warning("No IPv6 address will be assigned") - - names.hostip = hostip - names.hostip6 = hostip6 - - if serverrole is None: - serverrole = lp.get("server role") - - 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(paths.state_dir): - os.mkdir(paths.state_dir) - - if paths.sysvol and not os.path.exists(paths.sysvol): - os.makedirs(paths.sysvol, 0775) - - if not use_ntvfs and serverrole == "active directory domain controller": - s3conf = s3param.get_context() - s3conf.load(lp.configfile) - - if paths.sysvol is None: - raise MissingShareError("sysvol", paths.smbconf) - - file = tempfile.NamedTemporaryFile(dir=os.path.abspath(paths.sysvol)) - try: - try: - smbd.set_simple_acl(file.name, 0755, root_gid) - except Exception: - if not smbd.have_posix_acls(): - # This clue is only strictly correct for RPM and - # Debian-like Linux systems, but hopefully other users - # will get enough clue from it. - raise ProvisioningError("Samba was compiled without the posix ACL support that s3fs requires. Try installing libacl1-dev or libacl-devel, then re-run configure and make.") - - raise ProvisioningError("Your filesystem or build does not support posix ACLs, which s3fs requires. Try the mounting the filesystem with the 'acl' option.") - try: - smbd.chown(file.name, root_uid, root_gid) - except Exception: - raise ProvisioningError("Unable to chown a file on your filesystem. You may not be running provision as root.") - finally: - file.close() - - ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="") - - schema = Schema(domainsid, invocationid=invocationid, - schemadn=names.schemadn) - - if backend_type == "ldb": - provision_backend = LDBBackend(backend_type, paths=paths, - lp=lp, credentials=credentials, - names=names, logger=logger) - elif backend_type == "existing": - # If support for this is ever added back, then the URI will need to be - # specified again - provision_backend = ExistingBackend(backend_type, paths=paths, - lp=lp, credentials=credentials, - names=names, logger=logger, - ldap_backend_forced_uri=None) - elif backend_type == "fedora-ds": - provision_backend = FDSBackend(backend_type, paths=paths, - lp=lp, credentials=credentials, - names=names, logger=logger, domainsid=domainsid, - schema=schema, hostname=hostname, ldapadminpass=ldapadminpass, - slapd_path=slapd_path, - root=root) - elif backend_type == "openldap": - provision_backend = OpenLDAPBackend(backend_type, paths=paths, - lp=lp, credentials=credentials, - names=names, logger=logger, domainsid=domainsid, - schema=schema, hostname=hostname, ldapadminpass=ldapadminpass, - slapd_path=slapd_path, ol_mmr_urls=ol_mmr_urls) - else: - 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): - 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")) - - logger.info("Setting up secrets.ldb") - secrets_ldb = setup_secretsdb(paths, - session_info=session_info, - backend_credentials=provision_backend.secrets_credentials, lp=lp) - - try: - logger.info("Setting up the registry") - setup_registry(paths.hklm, session_info, lp=lp) - - logger.info("Setting up the privileges database") - setup_privileges(paths.privilege, session_info, lp=lp) - - logger.info("Setting up idmap db") - idmap = setup_idmapdb(paths.idmapdb, session_info=session_info, lp=lp) - - setup_name_mappings(idmap, sid=str(domainsid), - root_uid=root_uid, nobody_uid=nobody_uid, - users_gid=users_gid, root_gid=root_gid) - - logger.info("Setting up SAM db") - samdb = setup_samdb(paths.samdb, session_info, - provision_backend, lp, names, logger=logger, - serverrole=serverrole, - schema=schema, fill=samdb_fill, am_rodc=am_rodc) - - if serverrole == "active directory domain controller": - if paths.netlogon is None: - raise MissingShareError("netlogon", paths.smbconf) - - if paths.sysvol is None: - raise MissingShareError("sysvol", paths.smbconf) - - if not os.path.isdir(paths.netlogon): - os.makedirs(paths.netlogon, 0755) - - if adminpass is None: - adminpass = samba.generate_random_password(12, 32) - adminpass_generated = True - else: - adminpass = unicode(adminpass, 'utf-8') - adminpass_generated = False - - if samdb_fill == FILL_FULL: - provision_fill(samdb, secrets_ldb, logger, names, paths, - schema=schema, targetdir=targetdir, samdb_fill=samdb_fill, - hostip=hostip, hostip6=hostip6, domainsid=domainsid, - next_rid=next_rid, dc_rid=dc_rid, adminpass=adminpass, - krbtgtpass=krbtgtpass, domainguid=domainguid, - policyguid=policyguid, policyguid_dc=policyguid_dc, - invocationid=invocationid, machinepass=machinepass, - ntdsguid=ntdsguid, dns_backend=dns_backend, - dnspass=dnspass, serverrole=serverrole, - dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc, - lp=lp, use_ntvfs=use_ntvfs, - skip_sysvolacl=skip_sysvolacl) - - create_krb5_conf(paths.krb5conf, - dnsdomain=names.dnsdomain, hostname=names.hostname, - realm=names.realm) - logger.info("A Kerberos configuration suitable for Samba 4 has been " - "generated at %s", paths.krb5conf) - - if serverrole == "active directory domain controller": - create_dns_update_list(lp, logger, paths) - - backend_result = provision_backend.post_setup() - provision_backend.shutdown() - - except: - secrets_ldb.transaction_cancel() - raise - - # Now commit the secrets.ldb to disk - secrets_ldb.transaction_commit() - - # the commit creates the dns.keytab, now chown it - dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab) - if os.path.isfile(dns_keytab_path) and paths.bind_gid is not None: - try: - os.chmod(dns_keytab_path, 0640) - os.chown(dns_keytab_path, -1, paths.bind_gid) - except OSError: - if not os.environ.has_key('SAMBA_SELFTEST'): - logger.info("Failed to chown %s to bind gid %u", - dns_keytab_path, paths.bind_gid) - - result = ProvisionResult() - result.server_role = serverrole - result.domaindn = domaindn - result.paths = paths - result.names = names - result.lp = lp - result.samdb = samdb - result.idmap = idmap - result.domainsid = str(domainsid) - - if samdb_fill == FILL_FULL: - result.adminpass_generated = adminpass_generated - result.adminpass = adminpass - else: - result.adminpass_generated = False - result.adminpass = None - - result.backend_result = backend_result - - if use_rfc2307: - provision_fake_ypserver(logger=logger, samdb=samdb, - domaindn=names.domaindn, netbiosname=names.netbiosname, - nisdomain=names.domain.lower(), maxuid=maxuid, maxgid=maxgid) - - return result - - -def provision_become_dc(smbconf=None, targetdir=None, - realm=None, rootdn=None, domaindn=None, schemadn=None, configdn=None, - serverdn=None, domain=None, hostname=None, domainsid=None, - adminpass=None, krbtgtpass=None, domainguid=None, policyguid=None, - policyguid_dc=None, invocationid=None, machinepass=None, dnspass=None, - dns_backend=None, root=None, nobody=None, users=None, - backup=None, serverrole=None, ldap_backend=None, - ldap_backend_type=None, sitename=None, debuglevel=1, use_ntvfs=False): - - logger = logging.getLogger("provision") - samba.set_debug_level(debuglevel) - - res = provision(logger, system_session(), None, - smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS, - realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn, - configdn=configdn, serverdn=serverdn, domain=domain, - hostname=hostname, hostip=None, domainsid=domainsid, - machinepass=machinepass, - serverrole="active directory domain controller", - sitename=sitename, dns_backend=dns_backend, dnspass=dnspass, - use_ntvfs=use_ntvfs) - res.lp.set("debuglevel", str(debuglevel)) - return res - - -def create_krb5_conf(path, dnsdomain, hostname, realm): - """Write out a file containing zone statements suitable for inclusion in a - named.conf file (including GSS-TSIG configuration). - - :param path: Path of the new named.conf file. - :param dnsdomain: DNS Domain name - :param hostname: Local hostname - :param realm: Realm name - """ - setup_file(setup_path("krb5.conf"), path, { - "DNSDOMAIN": dnsdomain, - "HOSTNAME": hostname, - "REALM": realm, - }) - - -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) - - -class MissingShareError(ProvisioningError): - - def __init__(self, name, smbconf): - super(MissingShareError, self).__init__( - "Existing smb.conf does not have a [%s] share, but you are " - "configuring a DC. Please remove %s or add the share manually." % - (name, smbconf)) diff --git a/source4/scripting/python/samba/provision/backend.py b/source4/scripting/python/samba/provision/backend.py deleted file mode 100644 index f88b0db89c..0000000000 --- a/source4/scripting/python/samba/provision/backend.py +++ /dev/null @@ -1,840 +0,0 @@ -# -# Unix SMB/CIFS implementation. -# backend code for provisioning a Samba4 server - -# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2008 -# Copyright (C) Andrew Bartlett <abartlet@samba.org> 2008-2009 -# Copyright (C) Oliver Liebel <oliver@itc.li> 2008-2009 -# -# Based on the original in EJS: -# Copyright (C) Andrew Tridgell <tridge@samba.org> 2005 -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. -# - -"""Functions for setting up a Samba configuration (LDB and LDAP backends).""" - -from base64 import b64encode -import errno -import ldb -import os -import sys -import uuid -import time -import shutil -import subprocess -import urllib - -from ldb import SCOPE_BASE, SCOPE_ONELEVEL, LdbError, timestring - -from samba import Ldb, read_and_sub_file, setup_file -from samba.credentials import Credentials, DONT_USE_KERBEROS -from samba.schema import Schema - - -class SlapdAlreadyRunning(Exception): - - def __init__(self, uri): - self.ldapi_uri = uri - super(SlapdAlreadyRunning, self).__init__("Another slapd Instance " - "seems already running on this host, listening to %s." % - self.ldapi_uri) - - -class BackendResult(object): - - def report_logger(self, logger): - """Rerport this result to a particular logger. - - """ - raise NotImplementedError(self.report_logger) - - -class LDAPBackendResult(BackendResult): - - def __init__(self, credentials, slapd_command_escaped, ldapdir): - self.credentials = credentials - self.slapd_command_escaped = slapd_command_escaped - self.ldapdir = ldapdir - - def report_logger(self, logger): - if self.credentials.get_bind_dn() is not None: - logger.info("LDAP Backend Admin DN: %s" % - self.credentials.get_bind_dn()) - else: - logger.info("LDAP Admin User: %s" % - self.credentials.get_username()) - - if self.slapd_command_escaped is not None: - # now display slapd_command_file.txt to show how slapd must be - # started next time - logger.info( - "Use later the following commandline to start slapd, then Samba:") - logger.info(self.slapd_command_escaped) - logger.info( - "This slapd-Commandline is also stored under: %s/ldap_backend_startup.sh", - self.ldapdir) - - -class ProvisionBackend(object): - - def __init__(self, backend_type, paths=None, lp=None, - credentials=None, names=None, logger=None): - """Provision a backend for samba4""" - self.paths = paths - self.lp = lp - self.credentials = credentials - self.names = names - self.logger = logger - - self.type = backend_type - - # Set a default - the code for "existing" below replaces this - self.ldap_backend_type = backend_type - - def init(self): - """Initialize the backend.""" - raise NotImplementedError(self.init) - - def start(self): - """Start the backend.""" - raise NotImplementedError(self.start) - - def shutdown(self): - """Shutdown the backend.""" - raise NotImplementedError(self.shutdown) - - def post_setup(self): - """Post setup. - - :return: A BackendResult or None - """ - raise NotImplementedError(self.post_setup) - - -class LDBBackend(ProvisionBackend): - - def init(self): - self.credentials = None - self.secrets_credentials = None - - # Wipe the old sam.ldb databases away - shutil.rmtree(self.paths.samdb + ".d", True) - - def start(self): - pass - - def shutdown(self): - pass - - def post_setup(self): - pass - - -class ExistingBackend(ProvisionBackend): - - def __init__(self, backend_type, paths=None, lp=None, - credentials=None, names=None, logger=None, ldapi_uri=None): - - super(ExistingBackend, self).__init__(backend_type=backend_type, - paths=paths, lp=lp, - credentials=credentials, names=names, logger=logger, - ldap_backend_forced_uri=ldapi_uri) - - def init(self): - # Check to see that this 'existing' LDAP backend in fact exists - ldapi_db = Ldb(self.ldapi_uri, credentials=self.credentials) - 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.secrets_credentials = self.credentials - - # For now, assume existing backends at least emulate OpenLDAP - self.ldap_backend_type = "openldap" - - -class LDAPBackend(ProvisionBackend): - - def __init__(self, backend_type, paths=None, lp=None, - credentials=None, names=None, logger=None, domainsid=None, - schema=None, hostname=None, ldapadminpass=None, - slapd_path=None, ldap_backend_extra_port=None, - ldap_backend_forced_uri=None, ldap_dryrun_mode=True): - - super(LDAPBackend, self).__init__(backend_type=backend_type, - paths=paths, lp=lp, - credentials=credentials, names=names, logger=logger) - - self.domainsid = domainsid - self.schema = schema - self.hostname = hostname - - self.ldapdir = os.path.join(paths.private_dir, "ldap") - self.ldapadminpass = ldapadminpass - - self.slapd_path = slapd_path - self.slapd_command = None - self.slapd_command_escaped = None - self.slapd_pid = os.path.join(self.ldapdir, "slapd.pid") - - self.ldap_backend_extra_port = ldap_backend_extra_port - self.ldap_dryrun_mode = ldap_dryrun_mode - - if ldap_backend_forced_uri is not None: - self.ldap_uri = ldap_backend_forced_uri - else: - self.ldap_uri = "ldapi://%s" % urllib.quote( - os.path.join(self.ldapdir, "ldapi"), safe="") - - if not os.path.exists(self.ldapdir): - os.mkdir(self.ldapdir) - - def init(self): - from samba.provision import ProvisioningError - # we will shortly start slapd with ldapi for final provisioning. first - # check with ldapsearch -> rootDSE via self.ldap_uri if another - # instance of slapd is already running - try: - ldapi_db = Ldb(self.ldap_uri) - ldapi_db.search(base="", scope=SCOPE_BASE, - expression="(objectClass=OpenLDAProotDSE)") - try: - f = open(self.slapd_pid, "r") - except IOError, err: - if err != errno.ENOENT: - raise - else: - try: - p = f.read() - finally: - f.close() - self.logger.info("Check for slapd process with PID: %s and terminate it manually." % p) - raise SlapdAlreadyRunning(self.ldap_uri) - except LdbError: - # XXX: We should never be catching all Ldb errors - pass - - # Try to print helpful messages when the user has not specified the - # path to slapd - if self.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(self.slapd_path): - self.logger.warning("Path (%s) to slapd does not exist!", - self.slapd_path) - - if not os.path.isdir(self.ldapdir): - os.makedirs(self.ldapdir, 0700) - - # 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 - schemadb_path = os.path.join(self.ldapdir, "schema-tmp.ldb") - try: - os.unlink(schemadb_path) - except OSError: - pass - - self.schema.write_to_tmp_ldb(schemadb_path) - - self.credentials = Credentials() - self.credentials.guess(self.lp) - # Kerberos to an ldapi:// backend makes no sense - self.credentials.set_kerberos_state(DONT_USE_KERBEROS) - self.credentials.set_password(self.ldapadminpass) - - self.secrets_credentials = Credentials() - self.secrets_credentials.guess(self.lp) - # Kerberos to an ldapi:// backend makes no sense - self.secrets_credentials.set_kerberos_state(DONT_USE_KERBEROS) - self.secrets_credentials.set_username("samba-admin") - self.secrets_credentials.set_password(self.ldapadminpass) - - self.provision() - - def provision(self): - pass - - def start(self): - from samba.provision import ProvisioningError - self.slapd_command_escaped = "\'" + "\' \'".join(self.slapd_command) + "\'" - f = open(os.path.join(self.ldapdir, "ldap_backend_startup.sh"), 'w') - try: - f.write("#!/bin/sh\n" + self.slapd_command_escaped + "\n") - finally: - f.close() - - # 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) - - count = 0 - while self.slapd.poll() is None: - # Wait until the socket appears - try: - ldapi_db = Ldb(self.ldap_uri, lp=self.lp, credentials=self.credentials) - 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: - time.sleep(1) - count = count + 1 - - if count > 15: - self.logger.error("Could not connect to slapd started with: %s" % "\'" + "\' \'".join(self.slapd_provision_command) + "\'") - raise ProvisioningError("slapd never accepted a connection within 15 seconds of starting") - - self.logger.error("Could not start slapd with: %s" % "\'" + "\' \'".join(self.slapd_provision_command) + "\'") - raise ProvisioningError("slapd died before we could make a connection to it") - - def shutdown(self): - # if an LDAP backend is in use, terminate slapd after final provision - # and check its proper termination - if self.slapd.poll() is None: - # Kill the slapd - if getattr(self.slapd, "terminate", None) is not None: - self.slapd.terminate() - else: - # Older python versions don't have .terminate() - import signal - os.kill(self.slapd.pid, signal.SIGTERM) - - # and now wait for it to die - self.slapd.communicate() - - def post_setup(self): - return LDAPBackendResult(self.credentials, self.slapd_command_escaped, - self.ldapdir) - - -class OpenLDAPBackend(LDAPBackend): - - def __init__(self, backend_type, paths=None, lp=None, - credentials=None, names=None, logger=None, domainsid=None, - schema=None, hostname=None, ldapadminpass=None, slapd_path=None, - ldap_backend_extra_port=None, ldap_dryrun_mode=True, - ol_mmr_urls=None, nosync=False, ldap_backend_forced_uri=None): - from samba.provision import setup_path - super(OpenLDAPBackend, self).__init__( backend_type=backend_type, - paths=paths, lp=lp, - credentials=credentials, names=names, logger=logger, - domainsid=domainsid, schema=schema, hostname=hostname, - ldapadminpass=ldapadminpass, slapd_path=slapd_path, - ldap_backend_extra_port=ldap_backend_extra_port, - ldap_backend_forced_uri=ldap_backend_forced_uri, - ldap_dryrun_mode=ldap_dryrun_mode) - - self.ol_mmr_urls = ol_mmr_urls - self.nosync = nosync - - self.slapdconf = os.path.join(self.ldapdir, "slapd.conf") - self.modulesconf = os.path.join(self.ldapdir, "modules.conf") - self.memberofconf = os.path.join(self.ldapdir, "memberof.conf") - self.olmmrserveridsconf = os.path.join(self.ldapdir, "mmr_serverids.conf") - self.olmmrsyncreplconf = os.path.join(self.ldapdir, "mmr_syncrepl.conf") - self.olcdir = os.path.join(self.ldapdir, "slapd.d") - self.olcseedldif = os.path.join(self.ldapdir, "olc_seed.ldif") - - self.schema = Schema(self.domainsid, - schemadn=self.names.schemadn, files=[ - setup_path("schema_samba4.ldif")]) - - def setup_db_config(self, dbdir): - """Setup a Berkeley database. - - :param dbdir: Database directory. - """ - from samba.provision import setup_path - 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}) - - def provision(self): - from samba.provision import ProvisioningError, setup_path - # Wipe the directories so we can start - shutil.rmtree(os.path.join(self.ldapdir, "db"), True) - - # Allow the test scripts to turn off fsync() for OpenLDAP as for TDB - # and LDB - nosync_config = "" - if self.nosync: - nosync_config = "dbnosync" - - lnkattr = self.schema.linked_attributes() - 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 = self.schema.ldb.search(expression="(&(objectclass=attributeSchema)(searchFlags:1.2.840.113556.1.4.803:=1))", base=self.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 self.ol_mmr_urls is not None: - # For now, make these equal - mmr_pass = self.ldapadminpass - - url_list = filter(None,self.ol_mmr_urls.split(',')) - for url in url_list: - self.logger.info("Using LDAP-URL: "+url) - if len(url_list) == 1: - raise ProvisioningError("At least 2 LDAP-URLs needed for MMR!") - - 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": self.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": self.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": self.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 self.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 = 500 - 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"), self.olcseedldif, - {"OLC_SERVER_ID_CONF": olc_serverids_config, - "OLC_PW": self.ldapadminpass, - "OLC_SYNCREPL_CONF": olc_syncrepl_seed_config}) - # end olc - - setup_file(setup_path("slapd.conf"), self.slapdconf, - {"DNSDOMAIN": self.names.dnsdomain, - "LDAPDIR": self.ldapdir, - "DOMAINDN": self.names.domaindn, - "CONFIGDN": self.names.configdn, - "SCHEMADN": self.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}) - - self.setup_db_config(os.path.join(self.ldapdir, "db", "user")) - self.setup_db_config(os.path.join(self.ldapdir, "db", "config")) - self.setup_db_config(os.path.join(self.ldapdir, "db", "schema")) - - if not os.path.exists(os.path.join(self.ldapdir, "db", "samba", "cn=samba")): - os.makedirs(os.path.join(self.ldapdir, "db", "samba", "cn=samba"), 0700) - - setup_file(setup_path("cn=samba.ldif"), - os.path.join(self.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(self.ldapdir, "db", "samba", "cn=samba", "cn=samba-admin.ldif"), - {"LDAPADMINPASS_B64": b64encode(self.ldapadminpass), - "UUID": str(uuid.uuid4()), - "LDAPTIME": timestring(int(time.time()))} ) - - if self.ol_mmr_urls is not None: - setup_file(setup_path("cn=replicator.ldif"), - os.path.join(self.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" - - f = open(setup_path(mapping), 'r') - try: - backend_schema_data = self.schema.convert_to_openldap( - "openldap", f.read()) - finally: - f.close() - assert backend_schema_data is not None - f = open(os.path.join(self.ldapdir, backend_schema), 'w') - try: - f.write(backend_schema_data) - finally: - f.close() - - # now we generate the needed strings to start slapd automatically, - if self.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 self.ol_mmr_urls is None: - server_port_string = "ldap://0.0.0.0:%d" % self.ldap_backend_extra_port - else: - server_port_string = "ldap://%s.%s:%d" (self.names.hostname, - self.names.dnsdomain, self.ldap_backend_extra_port) - else: - server_port_string = "" - - # Prepare the 'result' information - the commands to return in - # particular - self.slapd_provision_command = [self.slapd_path, "-F" + self.olcdir, - "-h"] - - # copy this command so we have two version, one with -d0 and only - # ldapi (or the forced ldap_uri), and one with all the listen commands - self.slapd_command = list(self.slapd_provision_command) - - self.slapd_provision_command.extend([self.ldap_uri, "-d0"]) - - uris = self.ldap_uri - if server_port_string is not "": - uris = uris + " " + server_port_string - - self.slapd_command.append(uris) - - # Set the username - done here because Fedora DS still uses the admin - # DN and simple bind - self.credentials.set_username("samba-admin") - - # Wipe the old sam.ldb databases away - shutil.rmtree(self.olcdir, True) - os.makedirs(self.olcdir, 0770) - - # 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 self.ldap_dryrun_mode: - sys.exit(0) - - slapd_cmd = [self.slapd_path, "-Ttest", "-n", "0", "-f", - self.slapdconf, "-F", self.olcdir] - retcode = subprocess.call(slapd_cmd, close_fds=True, shell=False) - - if retcode != 0: - self.logger.error("conversion from slapd.conf to cn=config failed slapd started with: %s" % "\'" + "\' \'".join(slapd_cmd) + "\'") - raise ProvisioningError("conversion from slapd.conf to cn=config failed") - - if not os.path.exists(os.path.join(self.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(self.slapdconf) - - -class FDSBackend(LDAPBackend): - - def __init__(self, backend_type, paths=None, lp=None, - credentials=None, names=None, logger=None, domainsid=None, - schema=None, hostname=None, ldapadminpass=None, slapd_path=None, - ldap_backend_extra_port=None, ldap_dryrun_mode=True, root=None, - setup_ds_path=None): - - from samba.provision import setup_path - - super(FDSBackend, self).__init__(backend_type=backend_type, - paths=paths, lp=lp, - credentials=credentials, names=names, logger=logger, - domainsid=domainsid, schema=schema, hostname=hostname, - ldapadminpass=ldapadminpass, slapd_path=slapd_path, - ldap_backend_extra_port=ldap_backend_extra_port, - ldap_backend_forced_uri=ldap_backend_forced_uri, - ldap_dryrun_mode=ldap_dryrun_mode) - - self.root = root - self.setup_ds_path = setup_ds_path - self.ldap_instance = self.names.netbiosname.lower() - - self.sambadn = "CN=Samba" - - self.fedoradsinf = os.path.join(self.ldapdir, "fedorads.inf") - self.partitions_ldif = os.path.join(self.ldapdir, - "fedorads-partitions.ldif") - self.sasl_ldif = os.path.join(self.ldapdir, "fedorads-sasl.ldif") - self.dna_ldif = os.path.join(self.ldapdir, "fedorads-dna.ldif") - self.pam_ldif = os.path.join(self.ldapdir, "fedorads-pam.ldif") - self.refint_ldif = os.path.join(self.ldapdir, "fedorads-refint.ldif") - self.linked_attrs_ldif = os.path.join(self.ldapdir, - "fedorads-linked-attributes.ldif") - self.index_ldif = os.path.join(self.ldapdir, "fedorads-index.ldif") - self.samba_ldif = os.path.join(self.ldapdir, "fedorads-samba.ldif") - - self.samba3_schema = setup_path( - "../../examples/LDAP/samba.schema") - self.samba3_ldif = os.path.join(self.ldapdir, "samba3.ldif") - - self.retcode = subprocess.call(["bin/oLschema2ldif", - "-I", self.samba3_schema, - "-O", self.samba3_ldif, - "-b", self.names.domaindn], - close_fds=True, shell=False) - - if self.retcode != 0: - raise Exception("Unable to convert Samba 3 schema.") - - self.schema = Schema( - self.domainsid, - schemadn=self.names.schemadn, - files=[setup_path("schema_samba4.ldif"), self.samba3_ldif], - additional_prefixmap=["1000:1.3.6.1.4.1.7165.2.1", - "1001:1.3.6.1.4.1.7165.2.2"]) - - def provision(self): - from samba.provision import ProvisioningError, setup_path - if self.ldap_backend_extra_port is not None: - serverport = "ServerPort=%d" % self.ldap_backend_extra_port - else: - serverport = "" - - setup_file(setup_path("fedorads.inf"), self.fedoradsinf, - {"ROOT": self.root, - "HOSTNAME": self.hostname, - "DNSDOMAIN": self.names.dnsdomain, - "LDAPDIR": self.ldapdir, - "DOMAINDN": self.names.domaindn, - "LDAP_INSTANCE": self.ldap_instance, - "LDAPMANAGERDN": self.names.ldapmanagerdn, - "LDAPMANAGERPASS": self.ldapadminpass, - "SERVERPORT": serverport}) - - setup_file(setup_path("fedorads-partitions.ldif"), - self.partitions_ldif, - {"CONFIGDN": self.names.configdn, - "SCHEMADN": self.names.schemadn, - "SAMBADN": self.sambadn, - }) - - setup_file(setup_path("fedorads-sasl.ldif"), self.sasl_ldif, - {"SAMBADN": self.sambadn, - }) - - setup_file(setup_path("fedorads-dna.ldif"), self.dna_ldif, - {"DOMAINDN": self.names.domaindn, - "SAMBADN": self.sambadn, - "DOMAINSID": str(self.domainsid), - }) - - setup_file(setup_path("fedorads-pam.ldif"), self.pam_ldif) - - lnkattr = self.schema.linked_attributes() - - f = open(setup_path("fedorads-refint-delete.ldif"), 'r') - try: - refint_config = f.read() - finally: - f.close() - 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 - - f = open(self.refint_ldif, 'w') - try: - f.write(refint_config) - finally: - f.close() - f = open(self.linked_attrs_ldif, 'w') - try: - f.write(memberof_config) - finally: - f.close() - - attrs = ["lDAPDisplayName"] - res = self.schema.ldb.search(expression="(&(objectclass=attributeSchema)(searchFlags:1.2.840.113556.1.4.803:=1))", base=self.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 }) - - f = open(self.index_ldif, 'w') - try: - f.write(index_config) - finally: - f.close() - - setup_file(setup_path("fedorads-samba.ldif"), self.samba_ldif, { - "SAMBADN": self.sambadn, - "LDAPADMINPASS": self.ldapadminpass - }) - - mapping = "schema-map-fedora-ds-1.0" - backend_schema = "99_ad.ldif" - - # Build a schema file in Fedora DS format - f = open(setup_path(mapping), 'r') - try: - backend_schema_data = self.schema.convert_to_openldap("fedora-ds", - f.read()) - finally: - f.close() - assert backend_schema_data is not None - f = open(os.path.join(self.ldapdir, backend_schema), 'w') - try: - f.write(backend_schema_data) - finally: - f.close() - - self.credentials.set_bind_dn(self.names.ldapmanagerdn) - - # Destory the target directory, or else setup-ds.pl will complain - fedora_ds_dir = os.path.join(self.ldapdir, - "slapd-" + self.ldap_instance) - shutil.rmtree(fedora_ds_dir, True) - - self.slapd_provision_command = [self.slapd_path, "-D", fedora_ds_dir, - "-i", self.slapd_pid] - # In the 'provision' command line, stay in the foreground so we can - # easily kill it - self.slapd_provision_command.append("-d0") - - #the command for the final run is the normal script - self.slapd_command = [os.path.join(self.ldapdir, - "slapd-" + self.ldap_instance, "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 self.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 self.setup_ds_path is None: - raise ProvisioningError("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(self.setup_ds_path): - self.logger.warning("Path (%s) to slapd does not exist!", - self.setup_ds_path) - - # Run the Fedora DS setup utility - retcode = subprocess.call([self.setup_ds_path, "--silent", "--file", - self.fedoradsinf], close_fds=True, shell=False) - if retcode != 0: - raise ProvisioningError("setup-ds failed") - - # Load samba-admin - retcode = subprocess.call([ - os.path.join(self.ldapdir, "slapd-" + self.ldap_instance, "ldif2db"), "-s", self.sambadn, "-i", self.samba_ldif], - close_fds=True, shell=False) - if retcode != 0: - raise ProvisioningError("ldif2db failed") - - def post_setup(self): - ldapi_db = Ldb(self.ldap_uri, credentials=self.credentials) - - # configure in-directory access control on Fedora DS via the aci - # attribute (over a direct ldapi:// socket) - aci = """(targetattr = "*") (version 3.0;acl "full access to all by samba-admin";allow (all)(userdn = "ldap:///CN=samba-admin,%s");)""" % self.sambadn - - m = ldb.Message() - m["aci"] = ldb.MessageElement([aci], ldb.FLAG_MOD_REPLACE, "aci") - - for dnstring in (self.names.domaindn, self.names.configdn, - self.names.schemadn): - m.dn = ldb.Dn(ldapi_db, dnstring) - ldapi_db.modify(m) - return LDAPBackendResult(self.credentials, self.slapd_command_escaped, - self.ldapdir) diff --git a/source4/scripting/python/samba/provision/common.py b/source4/scripting/python/samba/provision/common.py deleted file mode 100644 index f96704bcce..0000000000 --- a/source4/scripting/python/samba/provision/common.py +++ /dev/null @@ -1,82 +0,0 @@ - -# Unix SMB/CIFS implementation. -# utility functions for provisioning a Samba4 server - -# 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 -# -# Based on the original in EJS: -# Copyright (C) Andrew Tridgell <tridge@samba.org> 2005 -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. -# - -"""Functions for setting up a Samba configuration.""" - -__docformat__ = "restructuredText" - -import os -from samba import read_and_sub_file -from samba.param import setup_dir - - -def setup_path(file): - """Return an absolute path to the provision tempate file specified by file""" - return os.path.join(setup_dir(), file) - - -def setup_add_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]): - """Setup a ldb in the private dir. - - :param ldb: LDB file to import data into - :param ldif_path: Path of the LDIF file to load - :param subst_vars: Optional variables to subsitute in LDIF. - :param nocontrols: Optional list of controls, can be None for no controls - """ - assert isinstance(ldif_path, str) - data = read_and_sub_file(ldif_path, subst_vars) - ldb.add_ldif(data, controls) - - -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 ldif_path: LDIF file path. - :param subst_vars: Optional dictionary with substitution variables. - """ - data = read_and_sub_file(ldif_path, subst_vars) - ldb.modify_ldif(data, controls) - - -def setup_ldb(ldb, ldif_path, subst_vars): - """Import a LDIF a file into a LDB handle, optionally substituting - variables. - - :note: Either all LDIF data will be added or none (using transactions). - - :param ldb: LDB file to import into. - :param ldif_path: Path to the LDIF file. - :param subst_vars: Dictionary with substitution variables. - """ - assert ldb is not None - ldb.transaction_start() - try: - setup_add_ldif(ldb, ldif_path, subst_vars) - except: - ldb.transaction_cancel() - raise - else: - ldb.transaction_commit() diff --git a/source4/scripting/python/samba/provision/descriptor.py b/source4/scripting/python/samba/provision/descriptor.py deleted file mode 100644 index 32e91ed2b5..0000000000 --- a/source4/scripting/python/samba/provision/descriptor.py +++ /dev/null @@ -1,359 +0,0 @@ - -# Unix SMB/CIFS implementation. -# backend code for provisioning a Samba4 server - -# 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 -# Copyright (C) Amitay Isaacs <amitay@samba.org> 2011 -# -# Based on the original in EJS: -# Copyright (C) Andrew Tridgell <tridge@samba.org> 2005 -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. -# - -"""Functions for setting up a Samba configuration (security descriptors).""" - -from samba.dcerpc import security -from samba.ndr import ndr_pack - -# Descriptors of naming contexts and other important objects - -def sddl2binary(sddl_in, domain_sid, name_map): - sddl = "%s" % sddl_in - - for [name, sid] in name_map.items(): - sddl = sddl.replace(name, sid) - - sec = security.descriptor.from_sddl(sddl, domain_sid) - return ndr_pack(sec) - -def get_empty_descriptor(domain_sid, name_map={}): - sddl= "" - return sddl2binary(sddl, domain_sid, name_map) - -# "get_schema_descriptor" is located in "schema.py" - -def get_config_descriptor(domain_sid, name_map={}): - sddl = "O:EAG:EAD:(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;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \ - "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \ - "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \ - "(A;;RPLCLORC;;;AU)(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \ - "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;CIIO;RPWPCRCCLCLORCWOWDSDSW;;;DA)" \ - "(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)" \ - "(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)" - return sddl2binary(sddl, domain_sid, name_map) - -def get_config_partitions_descriptor(domain_sid, name_map={}): - sddl = "D:" \ - "(A;;LCLORC;;;AU)" \ - "(OA;;RP;e48d0154-bcf8-11d1-8702-00c04fb96050;;AU)" \ - "(OA;;RP;d31a8757-2447-4545-8081-3bb610cacbf2;;AU)" \ - "(OA;;RP;66171887-8f3c-11d0-afda-00c04fd930c9;;AU)" \ - "(OA;;RP;032160bf-9824-11d1-aec0-0000f80367c1;;AU)" \ - "(OA;;RP;789ee1eb-8c8e-4e4c-8cec-79b31b7617b5;;AU)" \ - "(OA;;RP;5706aeaf-b940-4fb2-bcfc-5268683ad9fe;;AU)" \ - "(A;;RPWPCRCCLCLORCWOWDSW;;;EA)" \ - "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \ - "(A;;CC;;;ED)" \ - "(OA;CIIO;WP;3df793df-9858-4417-a701-735a1ecebf74;bf967a8d-0de6-11d0-a285-00aa003049e2;BA)" \ - "S:" \ - "(AU;CISA;WPCRCCDCWOWDSDDT;;;WD)" - return sddl2binary(sddl, domain_sid, name_map) - -def get_config_sites_descriptor(domain_sid, name_map={}): - sddl = "D:" \ - "(A;;RPLCLORC;;;AU)" \ - "(OA;CIIO;SW;d31a8757-2447-4545-8081-3bb610cacbf2;f0f8ffab-1191-11d0-a060-00aa006c33ed;ER)" \ - "(A;;RPWPCRCCLCLORCWOWDSW;;;EA)" \ - "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \ - "S:" \ - "(AU;CISA;CCDCSDDT;;;WD)" \ - "(OU;CIIOSA;CR;;f0f8ffab-1191-11d0-a060-00aa006c33ed;WD)" \ - "(OU;CIIOSA;WP;f30e3bbe-9ff0-11d1-b603-0000f80367c1;bf967ab3-0de6-11d0-a285-00aa003049e2;WD)" \ - "(OU;CIIOSA;WP;f30e3bbf-9ff0-11d1-b603-0000f80367c1;bf967ab3-0de6-11d0-a285-00aa003049e2;WD)" \ - "(OU;CIIOSA;WP;3e10944c-c354-11d0-aff8-0000f80367c1;b7b13124-b82e-11d0-afee-0000f80367c1;WD)" - return sddl2binary(sddl, domain_sid, name_map) - -def get_config_ntds_quotas_descriptor(domain_sid, name_map={}): - sddl = "D:" \ - "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \ - "(A;;RPLCLORC;;;BA)" \ - "(OA;;CR;4ecc03fe-ffc0-4947-b630-eb672a8a9dbc;;WD)" - return sddl2binary(sddl, domain_sid, name_map) - -def get_config_delete_protected1_descriptor(domain_sid, name_map={}): - sddl = "D:AI" \ - "(A;;RPLCLORC;;;AU)" \ - "(A;;RPWPCRCCLCLORCWOWDSW;;;EA)" \ - "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" - return sddl2binary(sddl, domain_sid, name_map) - -def get_config_delete_protected1wd_descriptor(domain_sid, name_map={}): - sddl = "D:AI" \ - "(A;;RPLCLORC;;;WD)" \ - "(A;;RPWPCRCCLCLORCWOWDSW;;;EA)" \ - "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" - return sddl2binary(sddl, domain_sid, name_map) - -def get_config_delete_protected2_descriptor(domain_sid, name_map={}): - sddl = "D:AI" \ - "(A;;RPLCLORC;;;AU)" \ - "(A;;RPWPCRCCDCLCLORCWOWDSW;;;EA)" \ - "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" - return sddl2binary(sddl, domain_sid, name_map) - -def get_domain_descriptor(domain_sid, name_map={}): - 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)" - return sddl2binary(sddl, domain_sid, name_map) - -def get_domain_infrastructure_descriptor(domain_sid, name_map={}): - sddl = "D:" \ - "(A;;RPLCLORC;;;AU)" \ - "(A;;RPWPCRCCLCLORCWOWDSW;;;DA)" \ - "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \ - "S:" \ - "(AU;SA;WPCR;;;WD)" - return sddl2binary(sddl, domain_sid, name_map) - -def get_domain_builtin_descriptor(domain_sid, name_map={}): - sddl = "D:" \ - "(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:" \ - "(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)" - return sddl2binary(sddl, domain_sid, name_map) - -def get_domain_computers_descriptor(domain_sid, name_map={}): - sddl = "D:" \ - "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \ - "(A;;RPWPCRCCDCLCLORCWOWDSW;;;DA)" \ - "(OA;;CCDC;bf967a86-0de6-11d0-a285-00aa003049e2;;AO)" \ - "(OA;;CCDC;bf967aba-0de6-11d0-a285-00aa003049e2;;AO)" \ - "(OA;;CCDC;bf967a9c-0de6-11d0-a285-00aa003049e2;;AO)" \ - "(OA;;CCDC;bf967aa8-0de6-11d0-a285-00aa003049e2;;PO)" \ - "(A;;RPLCLORC;;;AU)" \ - "(OA;;CCDC;4828cc14-1437-45bc-9b07-ad6f015e5f28;;AO)" \ - "S:" - return sddl2binary(sddl, domain_sid, name_map) - -def get_domain_users_descriptor(domain_sid, name_map={}): - sddl = "D:" \ - "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \ - "(A;;RPWPCRCCDCLCLORCWOWDSW;;;DA)" \ - "(OA;;CCDC;bf967aba-0de6-11d0-a285-00aa003049e2;;AO)" \ - "(OA;;CCDC;bf967a9c-0de6-11d0-a285-00aa003049e2;;AO)" \ - "(OA;;CCDC;bf967aa8-0de6-11d0-a285-00aa003049e2;;PO)" \ - "(A;;RPLCLORC;;;AU)" \ - "(OA;;CCDC;4828cc14-1437-45bc-9b07-ad6f015e5f28;;AO)" \ - "S:" - return sddl2binary(sddl, domain_sid, name_map) - -def get_domain_controllers_descriptor(domain_sid, name_map={}): - sddl = "D:" \ - "(A;;RPLCLORC;;;AU)" \ - "(A;;RPWPCRCCLCLORCWOWDSW;;;DA)" \ - "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \ - "(A;;RPLCLORC;;;ED)" \ - "S:" \ - "(AU;SA;CCDCWOWDSDDT;;;WD)" \ - "(AU;CISA;WP;;;WD)" - return sddl2binary(sddl, domain_sid, name_map) - -def get_domain_delete_protected1_descriptor(domain_sid, name_map={}): - sddl = "D:AI" \ - "(A;;RPLCLORC;;;AU)" \ - "(A;;RPWPCRCCLCLORCWOWDSW;;;DA)" \ - "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" - return sddl2binary(sddl, domain_sid, name_map) - -def get_domain_delete_protected2_descriptor(domain_sid, name_map={}): - sddl = "D:AI" \ - "(A;;RPLCLORC;;;AU)" \ - "(A;;RPWPCRCCDCLCLORCWOWDSW;;;DA)" \ - "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" - return sddl2binary(sddl, domain_sid, name_map) - -def get_dns_partition_descriptor(domain_sid, name_map={}): - sddl = "O:SYG: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;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;1131f6ad-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)" - return sddl2binary(sddl, domain_sid, name_map) - -def get_dns_forest_microsoft_dns_descriptor(domain_sid, name_map={}): - sddl = "O:SYG:SYD:AI" \ - "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \ - "(A;CI;RPWPCRCCDCLCRCWOWDSDDTSW;;;ED)" - return sddl2binary(sddl, domain_sid, name_map) - -def get_dns_domain_microsoft_dns_descriptor(domain_sid, name_map={}): - sddl = "O:SYG:SYD:AI" \ - "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)" \ - "(A;CI;RPWPCRCCDCLCRCWOWDSDDTSW;;;DnsAdmins)" \ - "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \ - "(A;CI;RPWPCRCCDCLCRCWOWDSDDTSW;;;ED)" - return sddl2binary(sddl, domain_sid, name_map) diff --git a/source4/scripting/python/samba/provision/sambadns.py b/source4/scripting/python/samba/provision/sambadns.py deleted file mode 100644 index 4522683fe8..0000000000 --- a/source4/scripting/python/samba/provision/sambadns.py +++ /dev/null @@ -1,1135 +0,0 @@ -# Unix SMB/CIFS implementation. -# backend code for provisioning DNS for a Samba4 server -# -# Copyright (C) Kai Blin <kai@samba.org> 2011 -# Copyright (C) Amitay Isaacs <amitay@gmail.com> 2011 -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. -# - -"""DNS-related provisioning""" - -import os -import uuid -import shutil -import time -import ldb -from base64 import b64encode -import samba -from samba.tdb_util import tdb_copy -from samba.ndr import ndr_pack, ndr_unpack -from samba import setup_file -from samba.dcerpc import dnsp, misc, security -from samba.dsdb import ( - DS_DOMAIN_FUNCTION_2000, - DS_DOMAIN_FUNCTION_2003, - DS_DOMAIN_FUNCTION_2008_R2 - ) -from samba.provision.descriptor import ( - get_domain_descriptor, - get_domain_delete_protected1_descriptor, - get_domain_delete_protected2_descriptor, - get_dns_partition_descriptor, - get_dns_forest_microsoft_dns_descriptor, - get_dns_domain_microsoft_dns_descriptor - ) -from samba.provision.common import ( - setup_path, - setup_add_ldif, - setup_modify_ldif, - setup_ldb - ) - - -def get_domainguid(samdb, domaindn): - res = samdb.search(base=domaindn, scope=ldb.SCOPE_BASE, attrs=["objectGUID"]) - domainguid = str(ndr_unpack(misc.GUID, res[0]["objectGUID"][0])) - return domainguid - - -def get_dnsadmins_sid(samdb, domaindn): - res = samdb.search(base="CN=DnsAdmins,CN=Users,%s" % domaindn, scope=ldb.SCOPE_BASE, - attrs=["objectSid"]) - dnsadmins_sid = ndr_unpack(security.dom_sid, res[0]["objectSid"][0]) - return dnsadmins_sid - - -class ARecord(dnsp.DnssrvRpcRecord): - - def __init__(self, ip_addr, serial=1, ttl=900, rank=dnsp.DNS_RANK_ZONE): - super(ARecord, self).__init__() - self.wType = dnsp.DNS_TYPE_A - self.rank = rank - self.dwSerial = serial - self.dwTtlSeconds = ttl - self.data = ip_addr - - -class AAAARecord(dnsp.DnssrvRpcRecord): - - def __init__(self, ip6_addr, serial=1, ttl=900, rank=dnsp.DNS_RANK_ZONE): - super(AAAARecord, self).__init__() - self.wType = dnsp.DNS_TYPE_AAAA - self.rank = rank - self.dwSerial = serial - self.dwTtlSeconds = ttl - self.data = ip6_addr - - -class CNameRecord(dnsp.DnssrvRpcRecord): - - def __init__(self, cname, serial=1, ttl=900, rank=dnsp.DNS_RANK_ZONE): - super(CNameRecord, self).__init__() - self.wType = dnsp.DNS_TYPE_CNAME - self.rank = rank - self.dwSerial = serial - self.dwTtlSeconds = ttl - self.data = cname - - -class NSRecord(dnsp.DnssrvRpcRecord): - - def __init__(self, dns_server, serial=1, ttl=900, rank=dnsp.DNS_RANK_ZONE): - super(NSRecord, self).__init__() - self.wType = dnsp.DNS_TYPE_NS - self.rank = rank - self.dwSerial = serial - self.dwTtlSeconds = ttl - self.data = dns_server - - -class SOARecord(dnsp.DnssrvRpcRecord): - - def __init__(self, mname, rname, serial=1, refresh=900, retry=600, - expire=86400, minimum=3600, ttl=3600, rank=dnsp.DNS_RANK_ZONE): - super(SOARecord, self).__init__() - self.wType = dnsp.DNS_TYPE_SOA - self.rank = rank - self.dwSerial = serial - self.dwTtlSeconds = ttl - soa = dnsp.soa() - soa.serial = serial - soa.refresh = refresh - soa.retry = retry - soa.expire = expire - soa.mname = mname - soa.rname = rname - self.data = soa - - -class SRVRecord(dnsp.DnssrvRpcRecord): - - def __init__(self, target, port, priority=0, weight=100, serial=1, ttl=900, - rank=dnsp.DNS_RANK_ZONE): - super(SRVRecord, self).__init__() - self.wType = dnsp.DNS_TYPE_SRV - self.rank = rank - self.dwSerial = serial - self.dwTtlSeconds = ttl - srv = dnsp.srv() - srv.nameTarget = target - srv.wPort = port - srv.wPriority = priority - srv.wWeight = weight - self.data = srv - - -class TXTRecord(dnsp.DnssrvRpcRecord): - - def __init__(self, slist, serial=1, ttl=900, rank=dnsp.DNS_RANK_ZONE): - super(TXTRecord, self).__init__() - self.wType = dnsp.DNS_TYPE_TXT - self.rank = rank - self.dwSerial = serial - self.dwTtlSeconds = ttl - stringlist = dnsp.string_list() - stringlist.count = len(slist) - stringlist.str = slist - self.data = stringlist - - -class TypeProperty(dnsp.DnsProperty): - - def __init__(self, zone_type=dnsp.DNS_ZONE_TYPE_PRIMARY): - super(TypeProperty, self).__init__() - self.wDataLength = 1 - self.version = 1 - self.id = dnsp.DSPROPERTY_ZONE_TYPE - self.data = zone_type - - -class AllowUpdateProperty(dnsp.DnsProperty): - - def __init__(self, allow_update=dnsp.DNS_ZONE_UPDATE_SECURE): - super(AllowUpdateProperty, self).__init__() - self.wDataLength = 1 - self.version = 1 - self.id = dnsp.DSPROPERTY_ZONE_ALLOW_UPDATE - self.data = allow_update - - -class SecureTimeProperty(dnsp.DnsProperty): - - def __init__(self, secure_time=0): - super(SecureTimeProperty, self).__init__() - self.wDataLength = 1 - self.version = 1 - self.id = dnsp.DSPROPERTY_ZONE_SECURE_TIME - self.data = secure_time - - -class NorefreshIntervalProperty(dnsp.DnsProperty): - - def __init__(self, norefresh_interval=0): - super(NorefreshIntervalProperty, self).__init__() - self.wDataLength = 1 - self.version = 1 - self.id = dnsp.DSPROPERTY_ZONE_NOREFRESH_INTERVAL - self.data = norefresh_interval - - -class RefreshIntervalProperty(dnsp.DnsProperty): - - def __init__(self, refresh_interval=0): - super(RefreshIntervalProperty, self).__init__() - self.wDataLength = 1 - self.version = 1 - self.id = dnsp.DSPROPERTY_ZONE_REFRESH_INTERVAL - self.data = refresh_interval - - -class AgingStateProperty(dnsp.DnsProperty): - - def __init__(self, aging_enabled=0): - super(AgingStateProperty, self).__init__() - self.wDataLength = 1 - self.version = 1 - self.id = dnsp.DSPROPERTY_ZONE_AGING_STATE - self.data = aging_enabled - - -class AgingEnabledTimeProperty(dnsp.DnsProperty): - - def __init__(self, next_cycle_hours=0): - super(AgingEnabledTimeProperty, self).__init__() - self.wDataLength = 1 - self.version = 1; - self.id = dnsp.DSPROPERTY_ZONE_AGING_ENABLED_TIME - self.data = next_cycle_hours - - -def setup_dns_partitions(samdb, domainsid, domaindn, forestdn, configdn, - serverdn): - domainzone_dn = "DC=DomainDnsZones,%s" % domaindn - forestzone_dn = "DC=ForestDnsZones,%s" % forestdn - descriptor = get_dns_partition_descriptor(domainsid) - setup_add_ldif(samdb, setup_path("provision_dnszones_partitions.ldif"), { - "DOMAINZONE_DN": domainzone_dn, - "FORESTZONE_DN": forestzone_dn, - "SECDESC" : b64encode(descriptor) - }) - - domainzone_guid = get_domainguid(samdb, domainzone_dn) - forestzone_guid = get_domainguid(samdb, forestzone_dn) - - domainzone_guid = str(uuid.uuid4()) - forestzone_guid = str(uuid.uuid4()) - - domainzone_dns = ldb.Dn(samdb, domainzone_dn).canonical_ex_str().strip() - forestzone_dns = ldb.Dn(samdb, forestzone_dn).canonical_ex_str().strip() - - protected1_desc = get_domain_delete_protected1_descriptor(domainsid) - protected2_desc = get_domain_delete_protected2_descriptor(domainsid) - setup_add_ldif(samdb, setup_path("provision_dnszones_add.ldif"), { - "DOMAINZONE_DN": domainzone_dn, - "FORESTZONE_DN": forestzone_dn, - "DOMAINZONE_GUID": domainzone_guid, - "FORESTZONE_GUID": forestzone_guid, - "DOMAINZONE_DNS": domainzone_dns, - "FORESTZONE_DNS": forestzone_dns, - "CONFIGDN": configdn, - "SERVERDN": serverdn, - "LOSTANDFOUND_DESCRIPTOR": b64encode(protected2_desc), - "INFRASTRUCTURE_DESCRIPTOR": b64encode(protected1_desc), - }) - - setup_modify_ldif(samdb, setup_path("provision_dnszones_modify.ldif"), { - "CONFIGDN": configdn, - "SERVERDN": serverdn, - "DOMAINZONE_DN": domainzone_dn, - "FORESTZONE_DN": forestzone_dn, - }) - - -def add_dns_accounts(samdb, domaindn): - setup_add_ldif(samdb, setup_path("provision_dns_accounts_add.ldif"), { - "DOMAINDN": domaindn, - }) - - -def add_dns_container(samdb, domaindn, prefix, domain_sid, dnsadmins_sid, forest=False): - name_map = {'DnsAdmins': str(dnsadmins_sid)} - if forest is True: - sd_val = get_dns_forest_microsoft_dns_descriptor(domain_sid, - name_map=name_map) - else: - sd_val = get_dns_domain_microsoft_dns_descriptor(domain_sid, - name_map=name_map) - # CN=MicrosoftDNS,<PREFIX>,<DOMAINDN> - msg = ldb.Message(ldb.Dn(samdb, "CN=MicrosoftDNS,%s,%s" % (prefix, domaindn))) - msg["objectClass"] = ["top", "container"] - msg["nTSecurityDescriptor"] = ldb.MessageElement(sd_val, ldb.FLAG_MOD_ADD, - "nTSecurityDescriptor") - samdb.add(msg) - - -def add_rootservers(samdb, domaindn, prefix): - rootservers = {} - rootservers["a.root-servers.net"] = "198.41.0.4" - rootservers["b.root-servers.net"] = "192.228.79.201" - rootservers["c.root-servers.net"] = "192.33.4.12" - rootservers["d.root-servers.net"] = "128.8.10.90" - rootservers["e.root-servers.net"] = "192.203.230.10" - rootservers["f.root-servers.net"] = "192.5.5.241" - rootservers["g.root-servers.net"] = "192.112.36.4" - rootservers["h.root-servers.net"] = "128.63.2.53" - rootservers["i.root-servers.net"] = "192.36.148.17" - rootservers["j.root-servers.net"] = "192.58.128.30" - rootservers["k.root-servers.net"] = "193.0.14.129" - rootservers["l.root-servers.net"] = "199.7.83.42" - rootservers["m.root-servers.net"] = "202.12.27.33" - - rootservers_v6 = {} - rootservers_v6["a.root-servers.net"] = "2001:503:ba3e::2:30" - rootservers_v6["f.root-servers.net"] = "2001:500:2f::f" - rootservers_v6["h.root-servers.net"] = "2001:500:1::803f:235" - rootservers_v6["j.root-servers.net"] = "2001:503:c27::2:30" - rootservers_v6["k.root-servers.net"] = "2001:7fd::1" - rootservers_v6["m.root-servers.net"] = "2001:dc3::35" - - container_dn = "DC=RootDNSServers,CN=MicrosoftDNS,%s,%s" % (prefix, domaindn) - - # Add DC=RootDNSServers,CN=MicrosoftDNS,<PREFIX>,<DOMAINDN> - msg = ldb.Message(ldb.Dn(samdb, container_dn)) - props = [] - props.append(ndr_pack(TypeProperty(zone_type=dnsp.DNS_ZONE_TYPE_CACHE))) - props.append(ndr_pack(AllowUpdateProperty(allow_update=dnsp.DNS_ZONE_UPDATE_OFF))) - props.append(ndr_pack(SecureTimeProperty())) - props.append(ndr_pack(NorefreshIntervalProperty())) - props.append(ndr_pack(RefreshIntervalProperty())) - props.append(ndr_pack(AgingStateProperty())) - props.append(ndr_pack(AgingEnabledTimeProperty())) - msg["objectClass"] = ["top", "dnsZone"] - msg["cn"] = ldb.MessageElement("Zone", ldb.FLAG_MOD_ADD, "cn") - msg["dNSProperty"] = ldb.MessageElement(props, ldb.FLAG_MOD_ADD, "dNSProperty") - samdb.add(msg) - - # Add DC=@,DC=RootDNSServers,CN=MicrosoftDNS,<PREFIX>,<DOMAINDN> - record = [] - for rserver in rootservers: - record.append(ndr_pack(NSRecord(rserver, serial=0, ttl=0, rank=dnsp.DNS_RANK_ROOT_HINT))) - - msg = ldb.Message(ldb.Dn(samdb, "DC=@,%s" % container_dn)) - msg["objectClass"] = ["top", "dnsNode"] - msg["dnsRecord"] = ldb.MessageElement(record, ldb.FLAG_MOD_ADD, "dnsRecord") - samdb.add(msg) - - # Add DC=<rootserver>,DC=RootDNSServers,CN=MicrosoftDNS,<PREFIX>,<DOMAINDN> - for rserver in rootservers: - record = [ndr_pack(ARecord(rootservers[rserver], serial=0, ttl=0, rank=dnsp.DNS_RANK_ROOT_HINT))] - # Add AAAA record as well (How does W2K* add IPv6 records?) - #if rserver in rootservers_v6: - # record.append(ndr_pack(AAAARecord(rootservers_v6[rserver], serial=0, ttl=0))) - msg = ldb.Message(ldb.Dn(samdb, "DC=%s,%s" % (rserver, container_dn))) - msg["objectClass"] = ["top", "dnsNode"] - msg["dnsRecord"] = ldb.MessageElement(record, ldb.FLAG_MOD_ADD, "dnsRecord") - samdb.add(msg) - -def add_at_record(samdb, container_dn, prefix, hostname, dnsdomain, hostip, hostip6): - - fqdn_hostname = "%s.%s" % (hostname, dnsdomain) - - at_records = [] - - # SOA record - at_soa_record = SOARecord(fqdn_hostname, "hostmaster.%s" % dnsdomain) - at_records.append(ndr_pack(at_soa_record)) - - # NS record - at_ns_record = NSRecord(fqdn_hostname) - at_records.append(ndr_pack(at_ns_record)) - - if hostip is not None: - # A record - at_a_record = ARecord(hostip) - at_records.append(ndr_pack(at_a_record)) - - if hostip6 is not None: - # AAAA record - at_aaaa_record = AAAARecord(hostip6) - at_records.append(ndr_pack(at_aaaa_record)) - - msg = ldb.Message(ldb.Dn(samdb, "DC=@,%s" % container_dn)) - msg["objectClass"] = ["top", "dnsNode"] - msg["dnsRecord"] = ldb.MessageElement(at_records, ldb.FLAG_MOD_ADD, "dnsRecord") - samdb.add(msg) - - -def add_srv_record(samdb, container_dn, prefix, host, port): - srv_record = SRVRecord(host, port) - msg = ldb.Message(ldb.Dn(samdb, "%s,%s" % (prefix, container_dn))) - msg["objectClass"] = ["top", "dnsNode"] - msg["dnsRecord"] = ldb.MessageElement(ndr_pack(srv_record), ldb.FLAG_MOD_ADD, "dnsRecord") - samdb.add(msg) - - -def add_ns_record(samdb, container_dn, prefix, host): - ns_record = NSRecord(host) - msg = ldb.Message(ldb.Dn(samdb, "%s,%s" % (prefix, container_dn))) - msg["objectClass"] = ["top", "dnsNode"] - msg["dnsRecord"] = ldb.MessageElement(ndr_pack(ns_record), ldb.FLAG_MOD_ADD, "dnsRecord") - samdb.add(msg) - - -def add_ns_glue_record(samdb, container_dn, prefix, host): - ns_record = NSRecord(host, rank=dnsp.DNS_RANK_NS_GLUE) - msg = ldb.Message(ldb.Dn(samdb, "%s,%s" % (prefix, container_dn))) - msg["objectClass"] = ["top", "dnsNode"] - msg["dnsRecord"] = ldb.MessageElement(ndr_pack(ns_record), ldb.FLAG_MOD_ADD, "dnsRecord") - samdb.add(msg) - - -def add_cname_record(samdb, container_dn, prefix, host): - cname_record = CNameRecord(host) - msg = ldb.Message(ldb.Dn(samdb, "%s,%s" % (prefix, container_dn))) - msg["objectClass"] = ["top", "dnsNode"] - msg["dnsRecord"] = ldb.MessageElement(ndr_pack(cname_record), ldb.FLAG_MOD_ADD, "dnsRecord") - samdb.add(msg) - - -def add_host_record(samdb, container_dn, prefix, hostip, hostip6): - host_records = [] - if hostip: - a_record = ARecord(hostip) - host_records.append(ndr_pack(a_record)) - if hostip6: - aaaa_record = AAAARecord(hostip6) - host_records.append(ndr_pack(aaaa_record)) - if host_records: - msg = ldb.Message(ldb.Dn(samdb, "%s,%s" % (prefix, container_dn))) - msg["objectClass"] = ["top", "dnsNode"] - msg["dnsRecord"] = ldb.MessageElement(host_records, ldb.FLAG_MOD_ADD, "dnsRecord") - samdb.add(msg) - - -def add_domain_record(samdb, domaindn, prefix, dnsdomain, domainsid, dnsadmins_sid): - # DC=<DNSDOMAIN>,CN=MicrosoftDNS,<PREFIX>,<DOMAINDN> - sddl = "O:SYG:BAD:AI" \ - "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)" \ - "(A;;CC;;;AU)" \ - "(A;;RPLCLORC;;;WD)" \ - "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \ - "(A;CI;RPWPCRCCDCLCRCWOWDSDDTSW;;;ED)" \ - "(A;CIID;RPWPCRCCDCLCRCWOWDSDDTSW;;;%s)" \ - "(A;CIID;RPWPCRCCDCLCRCWOWDSDDTSW;;;ED)" \ - "(OA;CIID;RPWPCR;91e647de-d96f-4b70-9557-d63ff4f3ccd8;;PS)" \ - "(A;CIID;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \ - "(A;CIID;LC;;;RU)" \ - "(A;CIID;RPWPCRCCLCLORCWOWDSDSW;;;BA)" \ - "S:AI" % dnsadmins_sid - sec = security.descriptor.from_sddl(sddl, domainsid) - props = [] - props.append(ndr_pack(TypeProperty())) - props.append(ndr_pack(AllowUpdateProperty())) - props.append(ndr_pack(SecureTimeProperty())) - props.append(ndr_pack(NorefreshIntervalProperty(norefresh_interval=168))) - props.append(ndr_pack(RefreshIntervalProperty(refresh_interval=168))) - props.append(ndr_pack(AgingStateProperty())) - props.append(ndr_pack(AgingEnabledTimeProperty())) - msg = ldb.Message(ldb.Dn(samdb, "DC=%s,CN=MicrosoftDNS,%s,%s" % (dnsdomain, prefix, domaindn))) - msg["objectClass"] = ["top", "dnsZone"] - msg["ntSecurityDescriptor"] = ldb.MessageElement(ndr_pack(sec), ldb.FLAG_MOD_ADD, - "nTSecurityDescriptor") - msg["dNSProperty"] = ldb.MessageElement(props, ldb.FLAG_MOD_ADD, "dNSProperty") - samdb.add(msg) - - -def add_msdcs_record(samdb, forestdn, prefix, dnsforest): - # DC=_msdcs.<DNSFOREST>,CN=MicrosoftDNS,<PREFIX>,<FORESTDN> - msg = ldb.Message(ldb.Dn(samdb, "DC=_msdcs.%s,CN=MicrosoftDNS,%s,%s" % - (dnsforest, prefix, forestdn))) - msg["objectClass"] = ["top", "dnsZone"] - samdb.add(msg) - - -def add_dc_domain_records(samdb, domaindn, prefix, site, dnsdomain, hostname, - hostip, hostip6): - - fqdn_hostname = "%s.%s" % (hostname, dnsdomain) - - # Set up domain container - DC=<DNSDOMAIN>,CN=MicrosoftDNS,<PREFIX>,<DOMAINDN> - domain_container_dn = ldb.Dn(samdb, "DC=%s,CN=MicrosoftDNS,%s,%s" % - (dnsdomain, prefix, domaindn)) - - # DC=@ record - add_at_record(samdb, domain_container_dn, "DC=@", hostname, dnsdomain, - hostip, hostip6) - - # DC=<HOSTNAME> record - add_host_record(samdb, domain_container_dn, "DC=%s" % hostname, hostip, - hostip6) - - # DC=_kerberos._tcp record - add_srv_record(samdb, domain_container_dn, "DC=_kerberos._tcp", - fqdn_hostname, 88) - - # DC=_kerberos._tcp.<SITENAME>._sites record - add_srv_record(samdb, domain_container_dn, "DC=_kerberos._tcp.%s._sites" % - site, fqdn_hostname, 88) - - # DC=_kerberos._udp record - add_srv_record(samdb, domain_container_dn, "DC=_kerberos._udp", - fqdn_hostname, 88) - - # DC=_kpasswd._tcp record - add_srv_record(samdb, domain_container_dn, "DC=_kpasswd._tcp", - fqdn_hostname, 464) - - # DC=_kpasswd._udp record - add_srv_record(samdb, domain_container_dn, "DC=_kpasswd._udp", - fqdn_hostname, 464) - - # DC=_ldap._tcp record - add_srv_record(samdb, domain_container_dn, "DC=_ldap._tcp", fqdn_hostname, - 389) - - # DC=_ldap._tcp.<SITENAME>._sites record - add_srv_record(samdb, domain_container_dn, "DC=_ldap._tcp.%s._sites" % - site, fqdn_hostname, 389) - - # FIXME: The number of SRV records depend on the various roles this DC has. - # _gc and _msdcs records are added if the we are the forest dc and not subdomain dc - # - # Assumption: current DC is GC and add all the entries - - # DC=_gc._tcp record - add_srv_record(samdb, domain_container_dn, "DC=_gc._tcp", fqdn_hostname, - 3268) - - # DC=_gc._tcp.<SITENAME>,_sites record - add_srv_record(samdb, domain_container_dn, "DC=_gc._tcp.%s._sites" % site, - fqdn_hostname, 3268) - - # DC=_msdcs record - add_ns_glue_record(samdb, domain_container_dn, "DC=_msdcs", fqdn_hostname) - - # FIXME: Following entries are added only if DomainDnsZones and ForestDnsZones partitions - # are created - # - # Assumption: Additional entries won't hurt on os_level = 2000 - - # DC=_ldap._tcp.<SITENAME>._sites.DomainDnsZones - add_srv_record(samdb, domain_container_dn, - "DC=_ldap._tcp.%s._sites.DomainDnsZones" % site, fqdn_hostname, - 389) - - # DC=_ldap._tcp.<SITENAME>._sites.ForestDnsZones - add_srv_record(samdb, domain_container_dn, - "DC=_ldap._tcp.%s._sites.ForestDnsZones" % site, fqdn_hostname, - 389) - - # DC=_ldap._tcp.DomainDnsZones - add_srv_record(samdb, domain_container_dn, "DC=_ldap._tcp.DomainDnsZones", - fqdn_hostname, 389) - - # DC=_ldap._tcp.ForestDnsZones - add_srv_record(samdb, domain_container_dn, "DC=_ldap._tcp.ForestDnsZones", - fqdn_hostname, 389) - - # DC=DomainDnsZones - add_host_record(samdb, domain_container_dn, "DC=DomainDnsZones", hostip, - hostip6) - - # DC=ForestDnsZones - add_host_record(samdb, domain_container_dn, "DC=ForestDnsZones", hostip, - hostip6) - - -def add_dc_msdcs_records(samdb, forestdn, prefix, site, dnsforest, hostname, - hostip, hostip6, domainguid, ntdsguid): - - fqdn_hostname = "%s.%s" % (hostname, dnsforest) - - # Set up forest container - DC=<DNSDOMAIN>,CN=MicrosoftDNS,<PREFIX>,<DOMAINDN> - forest_container_dn = ldb.Dn(samdb, "DC=_msdcs.%s,CN=MicrosoftDNS,%s,%s" % - (dnsforest, prefix, forestdn)) - - # DC=@ record - add_at_record(samdb, forest_container_dn, "DC=@", hostname, dnsforest, - None, None) - - # DC=_kerberos._tcp.dc record - add_srv_record(samdb, forest_container_dn, "DC=_kerberos._tcp.dc", - fqdn_hostname, 88) - - # DC=_kerberos._tcp.<SITENAME>._sites.dc record - add_srv_record(samdb, forest_container_dn, - "DC=_kerberos._tcp.%s._sites.dc" % site, fqdn_hostname, 88) - - # DC=_ldap._tcp.dc record - add_srv_record(samdb, forest_container_dn, "DC=_ldap._tcp.dc", - fqdn_hostname, 389) - - # DC=_ldap._tcp.<SITENAME>._sites.dc record - add_srv_record(samdb, forest_container_dn, "DC=_ldap._tcp.%s._sites.dc" % - site, fqdn_hostname, 389) - - # DC=_ldap._tcp.<SITENAME>._sites.gc record - add_srv_record(samdb, forest_container_dn, "DC=_ldap._tcp.%s._sites.gc" % - site, fqdn_hostname, 3268) - - # DC=_ldap._tcp.gc record - add_srv_record(samdb, forest_container_dn, "DC=_ldap._tcp.gc", - fqdn_hostname, 3268) - - # DC=_ldap._tcp.pdc record - add_srv_record(samdb, forest_container_dn, "DC=_ldap._tcp.pdc", - fqdn_hostname, 389) - - # DC=gc record - add_host_record(samdb, forest_container_dn, "DC=gc", hostip, hostip6) - - # DC=_ldap._tcp.<DOMAINGUID>.domains record - add_srv_record(samdb, forest_container_dn, - "DC=_ldap._tcp.%s.domains" % domainguid, fqdn_hostname, 389) - - # DC=<NTDSGUID> - add_cname_record(samdb, forest_container_dn, "DC=%s" % ntdsguid, - fqdn_hostname) - - -def secretsdb_setup_dns(secretsdb, names, private_dir, realm, - dnsdomain, dns_keytab_path, dnspass): - """Add DNS specific bits to a secrets database. - - :param secretsdb: Ldb Handle to the secrets database - :param names: Names shortcut - :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, - "DNS_KEYTAB": dns_keytab_path, - "DNSPASS_B64": b64encode(dnspass), - "HOSTNAME": names.hostname, - "DNSNAME" : '%s.%s' % ( - names.netbiosname.lower(), names.dnsdomain.lower()) - }) - - -def create_dns_dir(logger, paths): - """Write out a DNS zone file, from the info in the current database. - - :param logger: Logger object - :param paths: paths object - """ - dns_dir = os.path.dirname(paths.dns) - - try: - shutil.rmtree(dns_dir, True) - except OSError: - pass - - os.mkdir(dns_dir, 0770) - - if paths.bind_gid is not None: - try: - os.chown(dns_dir, -1, paths.bind_gid) - # chmod needed to cope with umask - os.chmod(dns_dir, 0770) - except OSError: - if not os.environ.has_key('SAMBA_SELFTEST'): - logger.error("Failed to chown %s to bind gid %u" % ( - dns_dir, paths.bind_gid)) - - -def create_zone_file(lp, logger, paths, targetdir, dnsdomain, - hostip, hostip6, hostname, realm, domainguid, - ntdsguid, site): - """Write out a DNS zone file, from the info in the current database. - - :param paths: paths object - :param dnsdomain: DNS Domain name - :param domaindn: DN of the Domain - :param hostip: Local IPv4 IP - :param hostip6: Local IPv6 IP - :param hostname: Local hostname - :param realm: Realm name - :param domainguid: GUID of the domain. - :param ntdsguid: GUID of the hosts nTDSDSA record. - """ - assert isinstance(domainguid, str) - - 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 = "" - - # we need to freeze the zone while we update the contents - if targetdir is None: - rndc = ' '.join(lp.get("rndc command")) - os.system(rndc + " freeze " + lp.get("realm")) - - setup_file(setup_path("provision.zone"), paths.dns, { - "HOSTNAME": hostname, - "DNSDOMAIN": dnsdomain, - "REALM": realm, - "HOSTIP_BASE_LINE": hostip_base_line, - "HOSTIP_HOST_LINE": hostip_host_line, - "DOMAINGUID": domainguid, - "DATESTRING": time.strftime("%Y%m%d%H"), - "DEFAULTSITE": site, - "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, - }) - - if paths.bind_gid is not None: - try: - os.chown(paths.dns, -1, paths.bind_gid) - # chmod needed to cope with umask - os.chmod(paths.dns, 0664) - except OSError: - if not os.environ.has_key('SAMBA_SELFTEST'): - logger.error("Failed to chown %s to bind gid %u" % ( - paths.dns, paths.bind_gid)) - - if targetdir is None: - os.system(rndc + " unfreeze " + lp.get("realm")) - - -def create_samdb_copy(samdb, logger, paths, names, domainsid, domainguid): - """Create a copy of samdb and give write permissions to named for dns partitions - """ - private_dir = paths.private_dir - samldb_dir = os.path.join(private_dir, "sam.ldb.d") - dns_dir = os.path.dirname(paths.dns) - dns_samldb_dir = os.path.join(dns_dir, "sam.ldb.d") - - # Find the partitions and corresponding filenames - partfile = {} - res = samdb.search(base="@PARTITION", scope=ldb.SCOPE_BASE, attrs=["partition"]) - for tmp in res[0]["partition"]: - (nc, fname) = tmp.split(':') - partfile[nc.upper()] = fname - - # Create empty domain partition - domaindn = names.domaindn.upper() - domainpart_file = os.path.join(dns_dir, partfile[domaindn]) - try: - os.mkdir(dns_samldb_dir) - file(domainpart_file, 'w').close() - - # Fill the basedn and @OPTION records in domain partition - dom_ldb = samba.Ldb(domainpart_file) - domainguid_line = "objectGUID: %s\n-" % domainguid - descr = b64encode(get_domain_descriptor(domainsid)) - setup_add_ldif(dom_ldb, setup_path("provision_basedn.ldif"), { - "DOMAINDN" : names.domaindn, - "DOMAINGUID" : domainguid_line, - "DOMAINSID" : str(domainsid), - "DESCRIPTOR" : descr}) - setup_add_ldif(dom_ldb, - setup_path("provision_basedn_options.ldif"), None) - except: - logger.error( - "Failed to setup database for BIND, AD based DNS cannot be used") - raise - del partfile[domaindn] - - # Link dns partitions and metadata - domainzonedn = "DC=DOMAINDNSZONES,%s" % names.domaindn.upper() - forestzonedn = "DC=FORESTDNSZONES,%s" % names.rootdn.upper() - domainzone_file = partfile[domainzonedn] - forestzone_file = partfile[forestzonedn] - metadata_file = "metadata.tdb" - try: - os.link(os.path.join(samldb_dir, metadata_file), - os.path.join(dns_samldb_dir, metadata_file)) - os.link(os.path.join(private_dir, domainzone_file), - os.path.join(dns_dir, domainzone_file)) - os.link(os.path.join(private_dir, forestzone_file), - os.path.join(dns_dir, forestzone_file)) - except OSError: - logger.error( - "Failed to setup database for BIND, AD based DNS cannot be used") - raise - del partfile[domainzonedn] - del partfile[forestzonedn] - - # Copy root, config, schema partitions (and any other if any) - # Since samdb is open in the current process, copy them in a child process - try: - tdb_copy(os.path.join(private_dir, "sam.ldb"), - os.path.join(dns_dir, "sam.ldb")) - for nc in partfile: - pfile = partfile[nc] - tdb_copy(os.path.join(private_dir, pfile), - os.path.join(dns_dir, pfile)) - except: - logger.error( - "Failed to setup database for BIND, AD based DNS cannot be used") - raise - - # Give bind read/write permissions dns partitions - if paths.bind_gid is not None: - try: - os.chown(samldb_dir, -1, paths.bind_gid) - os.chmod(samldb_dir, 0750) - - for dirname, dirs, files in os.walk(dns_dir): - for d in dirs: - dpath = os.path.join(dirname, d) - os.chown(dpath, -1, paths.bind_gid) - os.chmod(dpath, 0770) - for f in files: - if f.endswith('.ldb') or f.endswith('.tdb'): - fpath = os.path.join(dirname, f) - os.chown(fpath, -1, paths.bind_gid) - os.chmod(fpath, 0660) - except OSError: - if not os.environ.has_key('SAMBA_SELFTEST'): - logger.error( - "Failed to set permissions to sam.ldb* files, fix manually") - else: - if not os.environ.has_key('SAMBA_SELFTEST'): - logger.warning("""Unable to find group id for BIND, - set permissions to sam.ldb* files manually""") - - -def create_dns_update_list(lp, logger, paths): - """Write out a dns_update_list file""" - # note that we use no variable substitution on this file - # the substitution is done at runtime by samba_dnsupdate, samba_spnupdate - setup_file(setup_path("dns_update_list"), paths.dns_update_list, None) - setup_file(setup_path("spn_update_list"), paths.spn_update_list, None) - - -def create_named_conf(paths, realm, dnsdomain, dns_backend): - """Write out a file containing zone statements suitable for inclusion in a - named.conf file (including GSS-TSIG configuration). - - :param paths: all paths - :param realm: Realm name - :param dnsdomain: DNS Domain name - :param dns_backend: DNS backend type - :param keytab_name: File name of DNS keytab file - """ - - if dns_backend == "BIND9_FLATFILE": - setup_file(setup_path("named.conf"), paths.namedconf, { - "DNSDOMAIN": dnsdomain, - "REALM": realm, - "ZONE_FILE": paths.dns, - "REALM_WC": "*." + ".".join(realm.split(".")[1:]), - "NAMED_CONF": paths.namedconf, - "NAMED_CONF_UPDATE": paths.namedconf_update - }) - - setup_file(setup_path("named.conf.update"), paths.namedconf_update) - - elif dns_backend == "BIND9_DLZ": - setup_file(setup_path("named.conf.dlz"), paths.namedconf, { - "NAMED_CONF": paths.namedconf, - "MODULESDIR" : samba.param.modules_dir(), - }) - - -def create_named_txt(path, realm, dnsdomain, dnsname, private_dir, - keytab_name): - """Write out a file containing zone statements suitable for inclusion in a - named.conf file (including GSS-TSIG configuration). - - :param path: Path of the new named.conf file. - :param realm: Realm name - :param dnsdomain: DNS Domain name - :param private_dir: Path to private directory - :param keytab_name: File name of DNS keytab file - """ - setup_file(setup_path("named.txt"), path, { - "DNSDOMAIN": dnsdomain, - "DNSNAME" : dnsname, - "REALM": realm, - "DNS_KEYTAB": keytab_name, - "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name), - "PRIVATE_DIR": private_dir - }) - - -def is_valid_dns_backend(dns_backend): - return dns_backend in ("BIND9_FLATFILE", "BIND9_DLZ", "SAMBA_INTERNAL", "NONE") - - -def is_valid_os_level(os_level): - return DS_DOMAIN_FUNCTION_2000 <= os_level <= DS_DOMAIN_FUNCTION_2008_R2 - - -def create_dns_legacy(samdb, domainsid, forestdn, dnsadmins_sid): - # Set up MicrosoftDNS container - add_dns_container(samdb, forestdn, "CN=System", domainsid, dnsadmins_sid) - # Add root servers - add_rootservers(samdb, forestdn, "CN=System") - - -def fill_dns_data_legacy(samdb, domainsid, forestdn, dnsdomain, site, hostname, - hostip, hostip6, dnsadmins_sid): - # Add domain record - add_domain_record(samdb, forestdn, "CN=System", dnsdomain, domainsid, - dnsadmins_sid) - - # Add DNS records for a DC in domain - add_dc_domain_records(samdb, forestdn, "CN=System", site, dnsdomain, - hostname, hostip, hostip6) - - -def create_dns_partitions(samdb, domainsid, names, domaindn, forestdn, - dnsadmins_sid): - # Set up additional partitions (DomainDnsZones, ForstDnsZones) - setup_dns_partitions(samdb, domainsid, domaindn, forestdn, - names.configdn, names.serverdn) - - # Set up MicrosoftDNS containers - add_dns_container(samdb, domaindn, "DC=DomainDnsZones", domainsid, - dnsadmins_sid) - add_dns_container(samdb, forestdn, "DC=ForestDnsZones", domainsid, - dnsadmins_sid, forest=True) - - -def fill_dns_data_partitions(samdb, domainsid, site, domaindn, forestdn, - dnsdomain, dnsforest, hostname, hostip, hostip6, - domainguid, ntdsguid, dnsadmins_sid, autofill=True): - """Fill data in various AD partitions - - :param samdb: LDB object connected to sam.ldb file - :param domainsid: Domain SID (as dom_sid object) - :param site: Site name to create hostnames in - :param domaindn: DN of the domain - :param forestdn: DN of the forest - :param dnsdomain: DNS name of the domain - :param dnsforest: DNS name of the forest - :param hostname: Host name of this DC - :param hostip: IPv4 addresses - :param hostip6: IPv6 addresses - :param domainguid: Domain GUID - :param ntdsguid: NTDS GUID - :param dnsadmins_sid: SID for DnsAdmins group - :param autofill: Create DNS records (using fixed template) - """ - - ##### Set up DC=DomainDnsZones,<DOMAINDN> - # Add rootserver records - add_rootservers(samdb, domaindn, "DC=DomainDnsZones") - - # Add domain record - add_domain_record(samdb, domaindn, "DC=DomainDnsZones", dnsdomain, - domainsid, dnsadmins_sid) - - # Add DNS records for a DC in domain - if autofill: - add_dc_domain_records(samdb, domaindn, "DC=DomainDnsZones", site, - dnsdomain, hostname, hostip, hostip6) - - ##### Set up DC=ForestDnsZones,<DOMAINDN> - # Add _msdcs record - add_msdcs_record(samdb, forestdn, "DC=ForestDnsZones", dnsforest) - - # Add DNS records for a DC in forest - if autofill: - add_dc_msdcs_records(samdb, forestdn, "DC=ForestDnsZones", site, - dnsforest, hostname, hostip, hostip6, - domainguid, ntdsguid) - - -def setup_ad_dns(samdb, secretsdb, domainsid, names, paths, lp, logger, - dns_backend, os_level, site, dnspass=None, hostip=None, hostip6=None, - targetdir=None): - """Provision DNS information (assuming GC role) - - :param samdb: LDB object connected to sam.ldb file - :param secretsdb: LDB object connected to secrets.ldb file - :param domainsid: Domain SID (as dom_sid object) - :param names: Names shortcut - :param paths: Paths shortcut - :param lp: Loadparm object - :param logger: Logger object - :param dns_backend: Type of DNS backend - :param os_level: Functional level (treated as os level) - :param site: Site to create hostnames in - :param dnspass: Password for bind's DNS account - :param hostip: IPv4 address - :param hostip6: IPv6 address - :param targetdir: Target directory for creating DNS-related files for BIND9 - """ - - if not is_valid_dns_backend(dns_backend): - raise Exception("Invalid dns backend: %r" % dns_backend) - - if not is_valid_os_level(os_level): - raise Exception("Invalid os level: %r" % os_level) - - if dns_backend == "NONE": - logger.info("No DNS backend set, not configuring DNS") - return - - # Add dns accounts (DnsAdmins, DnsUpdateProxy) in domain - logger.info("Adding DNS accounts") - add_dns_accounts(samdb, names.domaindn) - - # If dns_backend is BIND9_FLATFILE - # Populate only CN=MicrosoftDNS,CN=System,<FORESTDN> - # - # If dns_backend is SAMBA_INTERNAL or BIND9_DLZ - # Populate DNS partitions - - # If os_level < 2003 (DS_DOMAIN_FUNCTION_2000) - # All dns records are in CN=MicrosoftDNS,CN=System,<FORESTDN> - # - # If os_level >= 2003 (DS_DOMAIN_FUNCTION_2003, DS_DOMAIN_FUNCTION_2008, - # DS_DOMAIN_FUNCTION_2008_R2) - # Root server records are in CN=MicrosoftDNS,CN=System,<FORESTDN> - # Domain records are in CN=MicrosoftDNS,CN=System,<FORESTDN> - # Domain records are in CN=MicrosoftDNS,DC=DomainDnsZones,<DOMAINDN> - # Forest records are in CN=MicrosoftDNS,DC=ForestDnsZones,<FORESTDN> - domaindn = names.domaindn - forestdn = samdb.get_root_basedn().get_linearized() - - dnsdomain = names.dnsdomain.lower() - dnsforest = dnsdomain - - hostname = names.netbiosname.lower() - - dnsadmins_sid = get_dnsadmins_sid(samdb, domaindn) - domainguid = get_domainguid(samdb, domaindn) - - # Create CN=System - logger.info("Creating CN=MicrosoftDNS,CN=System,%s" % forestdn) - create_dns_legacy(samdb, domainsid, forestdn, dnsadmins_sid) - - if os_level == DS_DOMAIN_FUNCTION_2000: - # Populating legacy dns - logger.info("Populating CN=MicrosoftDNS,CN=System,%s" % forestdn) - fill_dns_data_legacy(samdb, domainsid, forestdn, dnsdomain, site, - hostname, hostip, hostip6, dnsadmins_sid) - - elif dns_backend in ("SAMBA_INTERNAL", "BIND9_DLZ") and \ - os_level >= DS_DOMAIN_FUNCTION_2003: - - # Create DNS partitions - logger.info("Creating DomainDnsZones and ForestDnsZones partitions") - create_dns_partitions(samdb, domainsid, names, domaindn, forestdn, - dnsadmins_sid) - - # Populating dns partitions - logger.info("Populating DomainDnsZones and ForestDnsZones partitions") - fill_dns_data_partitions(samdb, domainsid, site, domaindn, forestdn, - dnsdomain, dnsforest, hostname, hostip, hostip6, - domainguid, names.ntdsguid, dnsadmins_sid) - - if dns_backend.startswith("BIND9_"): - setup_bind9_dns(samdb, secretsdb, domainsid, names, paths, lp, logger, - dns_backend, os_level, site=site, dnspass=dnspass, hostip=hostip, - hostip6=hostip6, targetdir=targetdir) - - -def setup_bind9_dns(samdb, secretsdb, domainsid, names, paths, lp, logger, - dns_backend, os_level, site=None, dnspass=None, hostip=None, - hostip6=None, targetdir=None): - """Provision DNS information (assuming BIND9 backend in DC role) - - :param samdb: LDB object connected to sam.ldb file - :param secretsdb: LDB object connected to secrets.ldb file - :param domainsid: Domain SID (as dom_sid object) - :param names: Names shortcut - :param paths: Paths shortcut - :param lp: Loadparm object - :param logger: Logger object - :param dns_backend: Type of DNS backend - :param os_level: Functional level (treated as os level) - :param site: Site to create hostnames in - :param dnspass: Password for bind's DNS account - :param hostip: IPv4 address - :param hostip6: IPv6 address - :param targetdir: Target directory for creating DNS-related files for BIND9 - """ - - if (not is_valid_dns_backend(dns_backend) or - not dns_backend.startswith("BIND9_")): - raise Exception("Invalid dns backend: %r" % dns_backend) - - if not is_valid_os_level(os_level): - raise Exception("Invalid os level: %r" % os_level) - - domaindn = names.domaindn - - domainguid = get_domainguid(samdb, domaindn) - - secretsdb_setup_dns(secretsdb, names, - paths.private_dir, realm=names.realm, - dnsdomain=names.dnsdomain, - dns_keytab_path=paths.dns_keytab, dnspass=dnspass) - - create_dns_dir(logger, paths) - - if dns_backend == "BIND9_FLATFILE": - create_zone_file(lp, logger, paths, targetdir, site=site, - dnsdomain=names.dnsdomain, hostip=hostip, - hostip6=hostip6, hostname=names.hostname, - realm=names.realm, domainguid=domainguid, - ntdsguid=names.ntdsguid) - - if dns_backend == "BIND9_DLZ" and os_level >= DS_DOMAIN_FUNCTION_2003: - create_samdb_copy(samdb, logger, paths, names, domainsid, domainguid) - - create_named_conf(paths, realm=names.realm, - dnsdomain=names.dnsdomain, dns_backend=dns_backend) - - create_named_txt(paths.namedtxt, - realm=names.realm, dnsdomain=names.dnsdomain, - dnsname = "%s.%s" % (names.hostname, names.dnsdomain), - private_dir=paths.private_dir, - keytab_name=paths.dns_keytab) - logger.info("See %s for an example configuration include file for BIND", - paths.namedconf) - logger.info("and %s for further documentation required for secure DNS " - "updates", paths.namedtxt) |