X-Git-Url: http://git.samba.org/samba.git/?a=blobdiff_plain;f=python%2Fsamba%2Fprovision%2F__init__.py;h=0a3a7b89cb7ae4a553bd831a5a73148c36f3b237;hb=1fb01c5b11fe4acfe1178f42cccb24136ae938e5;hp=24c2d55553dbf87395471aee2fbd6bf4676b71a9;hpb=91a5941f173caf3959df24c7702226b0088eeb86;p=nivanova%2Fsamba-autobuild%2F.git diff --git a/python/samba/provision/__init__.py b/python/samba/provision/__init__.py index 24c2d55553d..0a3a7b89cb7 100644 --- a/python/samba/provision/__init__.py +++ b/python/samba/provision/__init__.py @@ -26,6 +26,9 @@ __docformat__ = "restructuredText" +from samba.compat import urllib_quote +from samba.compat import string_types +from samba.compat import binary_type from base64 import b64encode import errno import os @@ -37,8 +40,6 @@ import logging import time import uuid import socket -import urllib -import string import tempfile import samba.dsdb @@ -46,6 +47,7 @@ import ldb from samba.auth import system_session, admin_session import samba +from samba import auth from samba.samba3 import smbd, passdb from samba.samba3 import param as s3param from samba.dsdb import DS_DOMAIN_FUNCTION_2000 @@ -59,27 +61,26 @@ from samba import ( valid_netbios_name, version, is_heimdal_built, - ) +) from samba.dcerpc import security, misc from samba.dcerpc.misc import ( SEC_CHAN_BDC, SEC_CHAN_WKSTA, - ) +) from samba.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.descriptor import ( get_empty_descriptor, get_config_descriptor, @@ -101,7 +102,7 @@ from samba.descriptor import ( get_dns_forest_microsoft_dns_descriptor, get_dns_domain_microsoft_dns_descriptor, get_managed_service_accounts_descriptor, - ) +) from samba.provision.common import ( setup_path, setup_add_ldif, @@ -115,7 +116,7 @@ from samba.provision.sambadns import ( get_dnsadmins_sid, setup_ad_dns, create_dns_update_list - ) +) import samba.param import samba.registry @@ -123,6 +124,7 @@ from samba.schema import Schema from samba.samdb import SamDB from samba.dbchecker import dbcheck from samba.provision.kerberos import create_kdc_conf +from samba.samdb import get_default_backend_store DEFAULT_POLICY_GUID = "31B2F340-016D-11D2-945F-00C04FB984F9" DEFAULT_DC_POLICY_GUID = "6AC1786C-016F-11D2-945F-00C04FB984F9" @@ -179,7 +181,7 @@ class ProvisionNames(object): def find_provision_key_parameters(samdb, secretsdb, idmapdb, paths, smbconf, - lp): + lp): """Get key provision parameters (realm, domain, ...) from a given provision :param samdb: An LDB object connected to the sam.ldb file @@ -194,45 +196,45 @@ def find_provision_key_parameters(samdb, secretsdb, idmapdb, paths, smbconf, names.adminpass = None # NT domain, kerberos realm, root dn, domain dn, domain dns name - names.domain = string.upper(lp.get("workgroup")) + names.domain = lp.get("workgroup").upper() names.realm = lp.get("realm") names.dnsdomain = names.realm.lower() basedn = samba.dn_from_dns_name(names.dnsdomain) - names.realm = string.upper(names.realm) + names.realm = names.realm.upper() # netbiosname # Get the netbiosname first (could be obtained from smb.conf in theory) res = secretsdb.search(expression="(flatname=%s)" % - names.domain,base="CN=Primary Domains", - scope=ldb.SCOPE_SUBTREE, attrs=["sAMAccountName"]) - names.netbiosname = str(res[0]["sAMAccountName"]).replace("$","") + names.domain, base="CN=Primary Domains", + scope=ldb.SCOPE_SUBTREE, attrs=["sAMAccountName"]) + names.netbiosname = str(res[0]["sAMAccountName"]).replace("$", "") names.smbconf = smbconf # That's a bit simplistic but it's ok as long as we have only 3 # partitions current = samdb.search(expression="(objectClass=*)", - base="", scope=ldb.SCOPE_BASE, - attrs=["defaultNamingContext", "schemaNamingContext", - "configurationNamingContext","rootDomainNamingContext", - "namingContexts"]) + base="", scope=ldb.SCOPE_BASE, + attrs=["defaultNamingContext", "schemaNamingContext", + "configurationNamingContext", "rootDomainNamingContext", + "namingContexts"]) - names.configdn = current[0]["configurationNamingContext"][0] - names.schemadn = current[0]["schemaNamingContext"][0] + names.configdn = str(current[0]["configurationNamingContext"][0]) + names.schemadn = str(current[0]["schemaNamingContext"][0]) if not (ldb.Dn(samdb, basedn) == (ldb.Dn(samdb, - current[0]["defaultNamingContext"][0]))): + current[0]["defaultNamingContext"][0].decode('utf8')))): raise ProvisioningError(("basedn in %s (%s) and from %s (%s)" "is not the same ..." % (paths.samdb, - str(current[0]["defaultNamingContext"][0]), - paths.smbconf, basedn))) + str(current[0]["defaultNamingContext"][0].decode('utf8')), + paths.smbconf, basedn))) - names.domaindn=current[0]["defaultNamingContext"][0] - names.rootdn=current[0]["rootDomainNamingContext"][0] - names.ncs=current[0]["namingContexts"] + names.domaindn = str(current[0]["defaultNamingContext"][0]) + names.rootdn = str(current[0]["rootDomainNamingContext"][0]) + names.ncs = current[0]["namingContexts"] names.dnsforestdn = None names.dnsdomaindn = None for i in range(0, len(names.ncs)): - nc = names.ncs[i] + nc = str(names.ncs[i]) dnsforestdn = "DC=ForestDnsZones,%s" % (str(names.rootdn)) if nc == dnsforestdn: @@ -246,7 +248,7 @@ def find_provision_key_parameters(samdb, secretsdb, idmapdb, paths, smbconf, # default site name res3 = samdb.search(expression="(objectClass=site)", - base="CN=Sites," + names.configdn, scope=ldb.SCOPE_ONELEVEL, attrs=["cn"]) + base="CN=Sites," + str(names.configdn), scope=ldb.SCOPE_ONELEVEL, attrs=["cn"]) names.sitename = str(res3[0]["cn"]) # dns hostname and server dn @@ -259,26 +261,26 @@ def find_provision_key_parameters(samdb, secretsdb, idmapdb, paths, smbconf, names.hostname = str(res4[0]["dNSHostName"]).replace("." + names.dnsdomain, "") server_res = samdb.search(expression="serverReference=%s" % res4[0].dn, - attrs=[], base=names.configdn) + attrs=[], base=names.configdn) names.serverdn = str(server_res[0].dn) # invocation id/objectguid res5 = samdb.search(expression="(objectClass=*)", - base="CN=NTDS Settings,%s" % str(names.serverdn), - scope=ldb.SCOPE_BASE, - attrs=["invocationID", "objectGUID"]) + base="CN=NTDS Settings,%s" % str(names.serverdn), + scope=ldb.SCOPE_BASE, + attrs=["invocationID", "objectGUID"]) names.invocation = str(ndr_unpack(misc.GUID, res5[0]["invocationId"][0])) names.ntdsguid = str(ndr_unpack(misc.GUID, res5[0]["objectGUID"][0])) # domain guid/sid res6 = samdb.search(expression="(objectClass=*)", base=basedn, - scope=ldb.SCOPE_BASE, attrs=["objectGUID", - "objectSid","msDS-Behavior-Version" ]) + scope=ldb.SCOPE_BASE, attrs=["objectGUID", + "objectSid", "msDS-Behavior-Version"]) names.domainguid = str(ndr_unpack(misc.GUID, res6[0]["objectGUID"][0])) - names.domainsid = ndr_unpack( security.dom_sid, res6[0]["objectSid"][0]) - names.forestsid = ndr_unpack( security.dom_sid, res6[0]["objectSid"][0]) + names.domainsid = ndr_unpack(security.dom_sid, res6[0]["objectSid"][0]) + names.forestsid = ndr_unpack(security.dom_sid, res6[0]["objectSid"][0]) if res6[0].get("msDS-Behavior-Version") is None or \ - int(res6[0]["msDS-Behavior-Version"][0]) < DS_DOMAIN_FUNCTION_2000: + int(res6[0]["msDS-Behavior-Version"][0]) < DS_DOMAIN_FUNCTION_2000: names.domainlevel = DS_DOMAIN_FUNCTION_2000 else: names.domainlevel = int(res6[0]["msDS-Behavior-Version"][0]) @@ -286,15 +288,15 @@ def find_provision_key_parameters(samdb, secretsdb, idmapdb, paths, smbconf, # policy guid res7 = samdb.search(expression="(name={%s})" % DEFAULT_POLICY_GUID, base="CN=Policies,CN=System," + basedn, - scope=ldb.SCOPE_ONELEVEL, attrs=["cn","displayName"]) - names.policyid = str(res7[0]["cn"]).replace("{","").replace("}","") + scope=ldb.SCOPE_ONELEVEL, attrs=["cn", "displayName"]) + names.policyid = str(res7[0]["cn"]).replace("{", "").replace("}", "") # dc policy guid res8 = samdb.search(expression="(name={%s})" % DEFAULT_DC_POLICY_GUID, base="CN=Policies,CN=System," + basedn, scope=ldb.SCOPE_ONELEVEL, - attrs=["cn","displayName"]) + attrs=["cn", "displayName"]) if len(res8) == 1: - names.policyid_dc = str(res8[0]["cn"]).replace("{","").replace("}","") + names.policyid_dc = str(res8[0]["cn"]).replace("{", "").replace("}", "") else: names.policyid_dc = None @@ -303,8 +305,8 @@ def find_provision_key_parameters(samdb, secretsdb, idmapdb, paths, smbconf, attrs=["xidNumber", "type"]) if len(res9) != 1: raise ProvisioningError("Unable to find uid/gid for Domain Admins rid (%s-%s" % (str(names.domainsid), security.DOMAIN_RID_ADMINISTRATOR)) - if res9[0]["type"][0] == "ID_TYPE_BOTH": - names.root_gid = res9[0]["xidNumber"][0] + if str(res9[0]["type"][0]) == "ID_TYPE_BOTH": + names.root_gid = int(res9[0]["xidNumber"][0]) else: names.root_gid = pwd.getpwuid(int(res9[0]["xidNumber"][0])).pw_gid @@ -362,15 +364,17 @@ def update_provision_usn(samdb, low, high, id, replace=False): scope=ldb.SCOPE_BASE, attrs=[LAST_PROVISION_USN_ATTRIBUTE, "dn"]) for e in entry[0][LAST_PROVISION_USN_ATTRIBUTE]: - if not re.search(';', e): - e = "%s;%s" % (e, id) + if not re.search(';', str(e)): + e = "%s;%s" % (str(e), id) tab.append(str(e)) tab.append("%s-%s;%s" % (low, high, id)) delta = ldb.Message() delta.dn = ldb.Dn(samdb, "@PROVISION") - delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab, - ldb.FLAG_MOD_REPLACE, LAST_PROVISION_USN_ATTRIBUTE) + delta[LAST_PROVISION_USN_ATTRIBUTE] = \ + ldb.MessageElement(tab, + ldb.FLAG_MOD_REPLACE, + LAST_PROVISION_USN_ATTRIBUTE) entry = samdb.search(expression='provisionnerID=*', base="@PROVISION", scope=ldb.SCOPE_BASE, attrs=["provisionnerID"]) @@ -396,12 +400,14 @@ def set_provision_usn(samdb, 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) + 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): +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 @@ -409,11 +415,11 @@ def get_max_usn(samdb,basedn): (ie. DC=foo, DC=bar) :return: The biggest USN in the provision""" - res = samdb.search(expression="objectClass=*",base=basedn, - scope=ldb.SCOPE_SUBTREE,attrs=["uSNChanged"], - controls=["search_options:1:2", - "server_sort:1:1:uSNChanged", - "paged_results:1:1"]) + res = samdb.search(expression="objectClass=*", base=basedn, + scope=ldb.SCOPE_SUBTREE, attrs=["uSNChanged"], + controls=["search_options:1:2", + "server_sort:1:1:uSNChanged", + "paged_results:1:1"]) return res[0]["uSNChanged"] @@ -426,8 +432,8 @@ def get_last_provision_usn(sam): """ try: entry = sam.search(expression="%s=*" % LAST_PROVISION_USN_ATTRIBUTE, - base="@PROVISION", scope=ldb.SCOPE_BASE, - attrs=[LAST_PROVISION_USN_ATTRIBUTE, "provisionnerID"]) + base="@PROVISION", scope=ldb.SCOPE_BASE, + attrs=[LAST_PROVISION_USN_ATTRIBUTE, "provisionnerID"]) except ldb.LdbError as e1: (ecode, emsg) = e1.args if ecode == ldb.ERR_NO_SUCH_OBJECT: @@ -506,7 +512,7 @@ def check_install(lp, session_info, credentials): if lp.get("realm") == "": raise Exception("Realm empty") samdb = Ldb(lp.samdb_url(), session_info=session_info, - credentials=credentials, lp=lp) + credentials=credentials, lp=lp) if len(samdb.search("(cn=Administrator)")) != 1: raise ProvisioningError("No administrator account found") @@ -526,8 +532,12 @@ def findnss(nssfn, names): raise KeyError("Unable to find user/group in %r" % names) -findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2] -findnss_gid = lambda names: findnss(grp.getgrnam, names)[2] +def findnss_uid(names): + return findnss(pwd.getpwnam, names)[2] + + +def findnss_gid(names): + return findnss(grp.getgrnam, names)[2] def provision_paths_from_lp(lp, dnsdomain): @@ -604,7 +614,9 @@ def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None, if dnsdomain is None: dnsdomain = lp.get("realm") if dnsdomain is None or dnsdomain == "": - raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp.configfile) + raise ProvisioningError( + "guess_names: 'realm' not specified in supplied %s!" % + lp.configfile) dnsdomain = dnsdomain.lower() @@ -672,7 +684,7 @@ def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None, dnsdomain = netbiosname.lower() if rootdn is None: - rootdn = domaindn + rootdn = domaindn if configdn is None: configdn = "CN=Configuration," + rootdn @@ -699,6 +711,7 @@ def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None, return names + def make_smbconf(smbconf, hostname, domain, realm, targetdir, serverrole=None, eadb=False, use_ntvfs=False, lp=None, global_param=None): @@ -725,11 +738,11 @@ def make_smbconf(smbconf, hostname, domain, realm, targetdir, "workgroup": domain, "realm": realm, "server role": serverrole, - } + } if lp is None: lp = samba.param.LoadParm() - #Load non-existent file + # Load non-existent file if os.path.exists(smbconf): lp.load(smbconf) @@ -743,10 +756,12 @@ def make_smbconf(smbconf, hostname, domain, realm, targetdir, global_settings["lock dir"] = os.path.abspath(targetdir) global_settings["state directory"] = os.path.abspath(os.path.join(targetdir, "state")) global_settings["cache directory"] = os.path.abspath(os.path.join(targetdir, "cache")) + global_settings["binddns dir"] = os.path.abspath(os.path.join(targetdir, "bind-dns")) lp.set("lock dir", os.path.abspath(targetdir)) - lp.set("state directory", global_settings["state directory"]) + lp.set("state directory", global_settings["state directory"]) lp.set("cache directory", global_settings["cache directory"]) + lp.set("binddns dir", global_settings["binddns dir"]) if eadb: if use_ntvfs and not lp.get("posix:eadb"): @@ -766,18 +781,18 @@ def make_smbconf(smbconf, hostname, domain, realm, targetdir, if serverrole == "active directory domain controller": shares["sysvol"] = os.path.join(lp.get("state directory"), "sysvol") shares["netlogon"] = os.path.join(shares["sysvol"], realm.lower(), - "scripts") + "scripts") else: global_settings["passdb backend"] = "samba_dsdb" f = open(smbconf, 'w') try: f.write("[globals]\n") - for key, val in global_settings.iteritems(): + for key, val in global_settings.items(): f.write("\t%s = %s\n" % (key, val)) f.write("\n") - for name, path in shares.iteritems(): + for name, path in shares.items(): f.write("[%s]\n" % name) f.write("\tpath = %s\n" % path) f.write("\tread only = no\n") @@ -814,7 +829,8 @@ def setup_name_mappings(idmap, sid, root_uid, nobody_uid, def setup_samdb_partitions(samdb_path, logger, lp, session_info, provision_backend, names, serverrole, - erase=False, plaintext_secrets=False): + erase=False, plaintext_secrets=False, + backend_store=None): """Setup the partitions for the SAM database. Alternatively, provision() may call this, and then populate the database. @@ -843,18 +859,32 @@ def setup_samdb_partitions(samdb_path, logger, lp, session_info, if provision_backend.type != "ldb": ldap_backend_line = "ldapBackend: %s" % provision_backend.ldap_uri - required_features = "# No required features" + required_features = None if not plaintext_secrets: required_features = "requiredFeatures: encryptedSecrets" + if backend_store is None: + backend_store = get_default_backend_store() + backend_store_line = "backendStore: %s" % backend_store + + if backend_store == "mdb": + if required_features is not None: + required_features += "\n" + else: + required_features = "" + required_features += "requiredFeatures: lmdbLevelOne" + + if required_features is None: + required_features = "# No required features" + samdb.transaction_start() try: logger.info("Setting up sam.ldb partitions and settings") setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), { - "LDAP_BACKEND_LINE": ldap_backend_line + "LDAP_BACKEND_LINE": ldap_backend_line, + "BACKEND_STORE": backend_store_line }) - setup_add_ldif(samdb, setup_path("provision_init.ldif"), { "BACKEND_TYPE": provision_backend.type, "SERVER_ROLE": serverrole, @@ -882,11 +912,11 @@ def secretsdb_self_join(secretsdb, domain, :param machinepass: Machine password """ attrs = ["whenChanged", - "secret", - "priorSecret", - "priorChanged", - "krb5Keytab", - "privateKeytab"] + "secret", + "priorSecret", + "priorChanged", + "krb5Keytab", + "privateKeytab"] if realm is not None: if dnsdomain is None: @@ -919,8 +949,8 @@ def secretsdb_self_join(secretsdb, domain, # but we don't delete the old record that we are about to modify, # because that would delete the keytab and previous password. res = secretsdb.search(base="cn=Primary Domains", attrs=attrs, - expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(distinguishedName=%s)))" % (domain, realm, str(domainsid), str(msg.dn))), - scope=ldb.SCOPE_ONELEVEL) + expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(distinguishedName=%s)))" % (domain, realm, str(domainsid), str(msg.dn))), + scope=ldb.SCOPE_ONELEVEL) for del_msg in res: secretsdb.delete(del_msg.dn) @@ -950,11 +980,11 @@ def secretsdb_self_join(secretsdb, domain, secretsdb.modify(msg) secretsdb.rename(res[0].dn, msg.dn) else: - spn = [ 'HOST/%s' % shortname ] + spn = ['HOST/%s' % shortname] if secure_channel_type == SEC_CHAN_BDC and dnsname is not None: # we are a domain controller then we add servicePrincipalName # entries for the keytab code to update. - spn.extend([ 'HOST/%s' % dnsname ]) + spn.extend(['HOST/%s' % dnsname]) msg["servicePrincipalName"] = spn secretsdb.add(msg) @@ -1001,17 +1031,17 @@ def setup_secretsdb(paths, session_info, backend_credentials, lp): backend_credentials.authentication_requested()): if backend_credentials.get_bind_dn() is not None: setup_add_ldif(secrets_ldb, - setup_path("secrets_simple_ldap.ldif"), { - "LDAPMANAGERDN": backend_credentials.get_bind_dn(), - "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password()) - }) + setup_path("secrets_simple_ldap.ldif"), { + "LDAPMANAGERDN": backend_credentials.get_bind_dn(), + "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password()).decode('utf8') + }) else: setup_add_ldif(secrets_ldb, - setup_path("secrets_sasl_ldap.ldif"), { - "LDAPADMINUSER": backend_credentials.get_username(), - "LDAPADMINREALM": backend_credentials.get_realm(), - "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password()) - }) + setup_path("secrets_sasl_ldap.ldif"), { + "LDAPADMINUSER": backend_credentials.get_username(), + "LDAPADMINREALM": backend_credentials.get_realm(), + "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password()).decode('utf8') + }) except: secrets_ldb.transaction_cancel() raise @@ -1033,6 +1063,7 @@ def setup_privileges(path, session_info, lp): privilege_ldb.erase() privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif")) + def setup_encrypted_secrets_key(path): """Setup the encrypted secrets key file. @@ -1053,7 +1084,7 @@ def setup_encrypted_secrets_key(path): finally: os.umask(umask_original) - with os.fdopen(fd, 'w') as f: + with os.fdopen(fd, 'wb') as f: key = samba.generate_random_bytes(16) f.write(key) @@ -1099,20 +1130,20 @@ def setup_samdb_rootdse(samdb, names): setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), { "SCHEMADN": names.schemadn, "DOMAINDN": names.domaindn, - "ROOTDN" : names.rootdn, + "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): + 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 + ntdsguid_line = "objectGUID: %s\n" % ntdsguid else: ntdsguid_line = "" @@ -1127,7 +1158,7 @@ def setup_self_join(samdb, admin_session_info, names, fill, machinepass, "INVOCATIONID": invocationid, "NETBIOSNAME": names.netbiosname, "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain), - "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')), + "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')).decode('utf8'), "DOMAINSID": str(domainsid), "DCRID": str(dc_rid), "SAMBA_VERSION_STRING": version, @@ -1154,7 +1185,7 @@ def setup_self_join(samdb, admin_session_info, names, fill, machinepass, "INVOCATIONID": invocationid, "NETBIOSNAME": names.netbiosname, "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain), - "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')), + "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')).decode('utf8'), "DOMAINSID": str(domainsid), "DCRID": str(dc_rid), "SAMBA_VERSION_STRING": version, @@ -1164,13 +1195,13 @@ def setup_self_join(samdb, admin_session_info, names, fill, machinepass, # Setup fSMORoleOwner entries to point at the newly created DC entry setup_modify_ldif(samdb, - setup_path("provision_self_join_modify_config.ldif"), { - "CONFIGDN": names.configdn, - "SCHEMADN": names.schemadn, - "DEFAULTSITE": names.sitename, - "NETBIOSNAME": names.netbiosname, - "SERVERDN": names.serverdn, - }) + setup_path("provision_self_join_modify_config.ldif"), { + "CONFIGDN": names.configdn, + "SCHEMADN": names.schemadn, + "DEFAULTSITE": names.sitename, + "NETBIOSNAME": names.netbiosname, + "SERVERDN": names.serverdn, + }) system_session_info = system_session() samdb.set_session_info(system_session_info) @@ -1191,9 +1222,9 @@ def setup_self_join(samdb, admin_session_info, names, fill, machinepass, setup_add_ldif(samdb, setup_path("provision_dns_add_samba.ldif"), { "DNSDOMAIN": names.dnsdomain, "DOMAINDN": names.domaindn, - "DNSPASS_B64": b64encode(dnspass.encode('utf-16-le')), - "HOSTNAME" : names.hostname, - "DNSNAME" : '%s.%s' % ( + "DNSPASS_B64": b64encode(dnspass.encode('utf-16-le')).decode('utf8'), + "HOSTNAME": names.hostname, + "DNSNAME": '%s.%s' % ( names.netbiosname.lower(), names.dnsdomain.lower()) }) @@ -1236,16 +1267,16 @@ def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc): :param policyguid: GUID of the default domain policy :param policyguid_dc: GUID of the default domain controler policy """ - policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid) + policy_path = getpolicypath(sysvolpath, dnsdomain, policyguid) create_gpo_struct(policy_path) - policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc) + policy_path = getpolicypath(sysvolpath, dnsdomain, policyguid_dc) create_gpo_struct(policy_path) def setup_samdb(path, session_info, provision_backend, lp, names, - logger, fill, serverrole, schema, am_rodc=False, - plaintext_secrets=False): + logger, fill, serverrole, schema, am_rodc=False, + plaintext_secrets=False, backend_store=None): """Setup a complete SAM Database. :note: This will wipe the main SAM database file! @@ -1253,8 +1284,9 @@ def setup_samdb(path, session_info, provision_backend, lp, names, # Also wipes the database setup_samdb_partitions(path, logger=logger, lp=lp, - provision_backend=provision_backend, session_info=session_info, - names=names, serverrole=serverrole, plaintext_secrets=plaintext_secrets) + provision_backend=provision_backend, session_info=session_info, + names=names, serverrole=serverrole, plaintext_secrets=plaintext_secrets, + backend_store=backend_store) # Load the database, but don's load the global schema and don't connect # quite yet @@ -1291,9 +1323,10 @@ def setup_samdb(path, session_info, provision_backend, lp, names, def fill_samdb(samdb, lp, names, logger, policyguid, - policyguid_dc, fill, adminpass, krbtgtpass, machinepass, dns_backend, - dnspass, invocationid, ntdsguid, serverrole, am_rodc=False, - dom_for_fun_level=None, schema=None, next_rid=None, dc_rid=None): + policyguid_dc, fill, adminpass, krbtgtpass, machinepass, dns_backend, + dnspass, invocationid, ntdsguid, serverrole, am_rodc=False, + dom_for_fun_level=None, schema=None, next_rid=None, dc_rid=None, + backend_store=None): if next_rid is None: next_rid = 1000 @@ -1333,7 +1366,7 @@ def fill_samdb(samdb, lp, names, logger, policyguid, samdb.set_opaque_integer("domainFunctionality", domainFunctionality) samdb.set_opaque_integer("forestFunctionality", forestFunctionality) samdb.set_opaque_integer("domainControllerFunctionality", - domainControllerFunctionality) + domainControllerFunctionality) samdb.set_domain_sid(str(names.domainsid)) samdb.set_invocation_id(invocationid) @@ -1348,7 +1381,7 @@ def fill_samdb(samdb, lp, names, logger, policyguid, else: domainguid_line = "" - descr = b64encode(get_domain_descriptor(names.domainsid)) + descr = b64encode(get_domain_descriptor(names.domainsid)).decode('utf8') setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), { "DOMAINDN": names.domaindn, "DOMAINSID": str(names.domainsid), @@ -1366,12 +1399,12 @@ def fill_samdb(samdb, lp, names, logger, policyguid, "DOMAIN_FUNCTIONALITY": str(domainFunctionality), "SAMBA_VERSION_STRING": version, "MIN_PWD_LENGTH": str(DEFAULT_MIN_PWD_LENGTH) - }) + }) # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it if fill == FILL_FULL: logger.info("Adding configuration container") - descr = b64encode(get_config_descriptor(names.domainsid)) + descr = b64encode(get_config_descriptor(names.domainsid)).decode('utf8') setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), { "CONFIGDN": names.configdn, "DESCRIPTOR": descr, @@ -1392,8 +1425,8 @@ def fill_samdb(samdb, lp, names, logger, policyguid, # Now register this container in the root of the forest msg = ldb.Message(ldb.Dn(samdb, names.domaindn)) - msg["subRefs"] = ldb.MessageElement(names.configdn , ldb.FLAG_MOD_ADD, - "subRefs") + msg["subRefs"] = ldb.MessageElement(names.configdn, ldb.FLAG_MOD_ADD, + "subRefs") samdb.invocation_id = invocationid @@ -1401,12 +1434,12 @@ def fill_samdb(samdb, lp, names, logger, policyguid, if fill == FILL_FULL: logger.info("Setting up sam.ldb configuration data") - partitions_descr = b64encode(get_config_partitions_descriptor(names.domainsid)) - sites_descr = b64encode(get_config_sites_descriptor(names.domainsid)) - ntdsquotas_descr = b64encode(get_config_ntds_quotas_descriptor(names.domainsid)) - protected1_descr = b64encode(get_config_delete_protected1_descriptor(names.domainsid)) - protected1wd_descr = b64encode(get_config_delete_protected1wd_descriptor(names.domainsid)) - protected2_descr = b64encode(get_config_delete_protected2_descriptor(names.domainsid)) + partitions_descr = b64encode(get_config_partitions_descriptor(names.domainsid)).decode('utf8') + sites_descr = b64encode(get_config_sites_descriptor(names.domainsid)).decode('utf8') + ntdsquotas_descr = b64encode(get_config_ntds_quotas_descriptor(names.domainsid)).decode('utf8') + protected1_descr = b64encode(get_config_delete_protected1_descriptor(names.domainsid)).decode('utf8') + protected1wd_descr = b64encode(get_config_delete_protected1wd_descriptor(names.domainsid)).decode('utf8') + protected2_descr = b64encode(get_config_delete_protected2_descriptor(names.domainsid)).decode('utf8') if "2008" in schema.base_schema: # exclude 2012-specific changes if we're using a 2008 schema @@ -1437,7 +1470,7 @@ def fill_samdb(samdb, lp, names, logger, policyguid, setup_add_ldif(samdb, setup_path("extended-rights.ldif"), { "CONFIGDN": names.configdn, - "INC2012" : incl_2012, + "INC2012": incl_2012, }) logger.info("Setting up display specifiers") @@ -1450,13 +1483,13 @@ def fill_samdb(samdb, lp, names, logger, policyguid, logger.info("Modifying display specifiers and extended rights") setup_modify_ldif(samdb, - setup_path("provision_configuration_modify.ldif"), { - "CONFIGDN": names.configdn, - "DISPLAYSPECIFIERS_DESCRIPTOR": protected2_descr - }) + setup_path("provision_configuration_modify.ldif"), { + "CONFIGDN": names.configdn, + "DISPLAYSPECIFIERS_DESCRIPTOR": protected2_descr + }) logger.info("Adding users container") - users_desc = b64encode(get_domain_users_descriptor(names.domainsid)) + users_desc = b64encode(get_domain_users_descriptor(names.domainsid)).decode('utf8') setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), { "DOMAINDN": names.domaindn, "USERS_DESCRIPTOR": users_desc @@ -1465,21 +1498,21 @@ def fill_samdb(samdb, lp, names, logger, policyguid, setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), { "DOMAINDN": names.domaindn}) logger.info("Adding computers container") - computers_desc = b64encode(get_domain_computers_descriptor(names.domainsid)) + computers_desc = b64encode(get_domain_computers_descriptor(names.domainsid)).decode('utf8') setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), { "DOMAINDN": names.domaindn, "COMPUTERS_DESCRIPTOR": computers_desc }) logger.info("Modifying computers container") setup_modify_ldif(samdb, - setup_path("provision_computers_modify.ldif"), { - "DOMAINDN": names.domaindn}) + setup_path("provision_computers_modify.ldif"), { + "DOMAINDN": names.domaindn}) logger.info("Setting up sam.ldb data") - infrastructure_desc = b64encode(get_domain_infrastructure_descriptor(names.domainsid)) - lostandfound_desc = b64encode(get_domain_delete_protected2_descriptor(names.domainsid)) - system_desc = b64encode(get_domain_delete_protected1_descriptor(names.domainsid)) - builtin_desc = b64encode(get_domain_builtin_descriptor(names.domainsid)) - controllers_desc = b64encode(get_domain_controllers_descriptor(names.domainsid)) + infrastructure_desc = b64encode(get_domain_infrastructure_descriptor(names.domainsid)).decode('utf8') + lostandfound_desc = b64encode(get_domain_delete_protected2_descriptor(names.domainsid)).decode('utf8') + system_desc = b64encode(get_domain_delete_protected1_descriptor(names.domainsid)).decode('utf8') + builtin_desc = b64encode(get_domain_builtin_descriptor(names.domainsid)).decode('utf8') + controllers_desc = b64encode(get_domain_controllers_descriptor(names.domainsid)).decode('utf8') setup_add_ldif(samdb, setup_path("provision.ldif"), { "CREATTIME": str(samba.unix2nttime(int(time.time()))), "DOMAINDN": names.domaindn, @@ -1494,22 +1527,22 @@ def fill_samdb(samdb, lp, names, logger, policyguid, "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: - managedservice_descr = b64encode(get_managed_service_accounts_descriptor(names.domainsid)) + managedservice_descr = b64encode(get_managed_service_accounts_descriptor(names.domainsid)).decode('utf8') setup_modify_ldif(samdb, setup_path("provision_configuration_references.ldif"), { - "CONFIGDN": names.configdn, - "SCHEMADN": names.schemadn}) + "CONFIGDN": names.configdn, + "SCHEMADN": names.schemadn}) logger.info("Setting up well known security principals") - protected1wd_descr = b64encode(get_config_delete_protected1wd_descriptor(names.domainsid)) + protected1wd_descr = b64encode(get_config_delete_protected1wd_descriptor(names.domainsid)).decode('utf8') setup_add_ldif(samdb, setup_path("provision_well_known_sec_princ.ldif"), { "CONFIGDN": names.configdn, "WELLKNOWNPRINCIPALS_DESCRIPTOR": protected1wd_descr, - }) + }, controls=["relax:0", "provision:0"]) if fill == FILL_FULL or fill == FILL_SUBDOMAIN: setup_modify_ldif(samdb, @@ -1522,45 +1555,46 @@ def fill_samdb(samdb, lp, names, logger, policyguid, setup_add_ldif(samdb, setup_path("provision_users.ldif"), { "DOMAINDN": names.domaindn, "DOMAINSID": str(names.domainsid), - "ADMINPASS_B64": b64encode(adminpass.encode('utf-16-le')), - "KRBTGTPASS_B64": b64encode(krbtgtpass.encode('utf-16-le')) - }) + "ADMINPASS_B64": b64encode(adminpass.encode('utf-16-le')).decode('utf8'), + "KRBTGTPASS_B64": b64encode(krbtgtpass.encode('utf-16-le')).decode('utf8') + }, controls=["relax:0", "provision:0"]) logger.info("Setting up self join") setup_self_join(samdb, admin_session_info, names=names, fill=fill, - invocationid=invocationid, - dns_backend=dns_backend, - dnspass=dnspass, - machinepass=machinepass, - domainsid=names.domainsid, - next_rid=next_rid, - dc_rid=dc_rid, - policyguid=policyguid, - policyguid_dc=policyguid_dc, - domainControllerFunctionality=domainControllerFunctionality, - ntdsguid=ntdsguid) + invocationid=invocationid, + dns_backend=dns_backend, + dnspass=dnspass, + machinepass=machinepass, + domainsid=names.domainsid, + next_rid=next_rid, + dc_rid=dc_rid, + policyguid=policyguid, + policyguid_dc=policyguid_dc, + domainControllerFunctionality=domainControllerFunctionality, + ntdsguid=ntdsguid) ntds_dn = "CN=NTDS Settings,%s" % names.serverdn names.ntdsguid = samdb.searchone(basedn=ntds_dn, - attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE) - assert isinstance(names.ntdsguid, str) + attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE).decode('utf8') + assert isinstance(names.ntdsguid, string_types) return samdb SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)" POLICIES_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)(A;OICI;0x001301bf;;;PA)" -SYSVOL_SERVICE="sysvol" +SYSVOL_SERVICE = "sysvol" + def set_dir_acl(path, acl, lp, domsid, use_ntvfs, passdb, service=SYSVOL_SERVICE): setntacl(lp, path, acl, domsid, use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service) for root, dirs, files in os.walk(path, topdown=False): for name in files: setntacl(lp, os.path.join(root, name), acl, domsid, - use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service) + use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service) for name in dirs: setntacl(lp, os.path.join(root, name), acl, domsid, - use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service) + use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service) def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb): @@ -1578,15 +1612,15 @@ def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, p # Set ACL for GPO root folder root_policy_path = os.path.join(sysvol, dnsdomain, "Policies") setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid), - use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=SYSVOL_SERVICE) + use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=SYSVOL_SERVICE) - res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn), - attrs=["cn", "nTSecurityDescriptor"], - expression="", scope=ldb.SCOPE_ONELEVEL) + res = samdb.search(base="CN=Policies,CN=System,%s" %(domaindn), + attrs=["cn", "nTSecurityDescriptor"], + expression="", scope=ldb.SCOPE_ONELEVEL) for policy in res: acl = ndr_unpack(security.descriptor, - str(policy["nTSecurityDescriptor"])).as_sddl() + policy["nTSecurityDescriptor"][0]).as_sddl() policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"])) set_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp, str(domainsid), use_ntvfs, @@ -1594,7 +1628,7 @@ def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, p def setsysvolacl(samdb, netlogon, sysvol, uid, gid, domainsid, dnsdomain, - domaindn, lp, use_ntvfs): + domaindn, lp, use_ntvfs): """Set the ACL for the sysvol share and the subfolders :param samdb: An LDB object on the SAM db @@ -1657,7 +1691,6 @@ def setsysvolacl(samdb, netlogon, sysvol, uid, gid, domainsid, dnsdomain, if domain_info["dns_domain"].upper() != dnsdomain.upper(): raise ProvisioningError('Realm as seen by pdb_samba_dsdb [%s] does not match Realm as seen by the provision script [%s]!' % (domain_info["dns_domain"].upper(), dnsdomain.upper())) - try: if use_ntvfs: os.chown(sysvol, -1, gid) @@ -1666,33 +1699,47 @@ def setsysvolacl(samdb, netlogon, sysvol, uid, gid, domainsid, dnsdomain, else: canchown = True + # use admin sid dn as user dn, since admin should own most of the files, + # the operation will be much faster + userdn = ''.format(domainsid, security.DOMAIN_RID_ADMINISTRATOR) + + flags = (auth.AUTH_SESSION_INFO_DEFAULT_GROUPS | + auth.AUTH_SESSION_INFO_AUTHENTICATED | + auth.AUTH_SESSION_INFO_SIMPLE_PRIVILEGES) + + session_info = auth.user_session(samdb, lp_ctx=lp, dn=userdn, + session_info_flags=flags) + + def _setntacl(path): + """A helper to reuse args""" + return setntacl( + lp, path, SYSVOL_ACL, str(domainsid), + use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=s4_passdb, + service=SYSVOL_SERVICE, session_info=session_info) + # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level) - setntacl(lp,sysvol, SYSVOL_ACL, str(domainsid), use_ntvfs=use_ntvfs, - skip_invalid_chown=True, passdb=s4_passdb, - service=SYSVOL_SERVICE) + _setntacl(sysvol) for root, dirs, files in os.walk(sysvol, topdown=False): for name in files: if use_ntvfs and canchown: os.chown(os.path.join(root, name), -1, gid) - setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid), - use_ntvfs=use_ntvfs, skip_invalid_chown=True, - passdb=s4_passdb, service=SYSVOL_SERVICE) + _setntacl(os.path.join(root, name)) for name in dirs: if use_ntvfs and canchown: os.chown(os.path.join(root, name), -1, gid) - setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid), - use_ntvfs=use_ntvfs, skip_invalid_chown=True, - passdb=s4_passdb, service=SYSVOL_SERVICE) + _setntacl(os.path.join(root, name)) # Set acls on Policy folder and policies folders set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb=s4_passdb) + 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) @@ -1704,7 +1751,9 @@ def check_dir_acl(path, acl, lp, domainsid, direct_db_access): fsacl = getntacl(lp, os.path.join(root, name), direct_db_access=direct_db_access, service=SYSVOL_SERVICE) if fsacl is None: - raise ProvisioningError('%s ACL on GPO file %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name))) + raise ProvisioningError('%s ACL on GPO file %s not found!' % + (acl_type(direct_db_access), + os.path.join(root, name))) fsacl_sddl = fsacl.as_sddl(domainsid) if fsacl_sddl != acl: raise ProvisioningError('%s ACL on GPO file %s %s does not match expected value %s from GPO object' % (acl_type(direct_db_access), os.path.join(root, name), fsacl_sddl, acl)) @@ -1713,14 +1762,16 @@ def check_dir_acl(path, acl, lp, domainsid, direct_db_access): fsacl = getntacl(lp, os.path.join(root, name), direct_db_access=direct_db_access, service=SYSVOL_SERVICE) if fsacl is None: - raise ProvisioningError('%s ACL on GPO directory %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name))) + raise ProvisioningError('%s ACL on GPO directory %s not found!' + % (acl_type(direct_db_access), + os.path.join(root, name))) fsacl_sddl = fsacl.as_sddl(domainsid) if fsacl_sddl != acl: raise ProvisioningError('%s ACL on GPO directory %s %s does not match expected value %s from GPO object' % (acl_type(direct_db_access), os.path.join(root, name), fsacl_sddl, acl)) def check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, - direct_db_access): + direct_db_access): """Set ACL on the sysvol//Policies folder and the policy folders beneath. @@ -1741,20 +1792,20 @@ def check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, fsacl_sddl = fsacl.as_sddl(domainsid) if fsacl_sddl != POLICIES_ACL: raise ProvisioningError('%s ACL on policy root %s %s does not match expected value %s from provision' % (acl_type(direct_db_access), root_policy_path, fsacl_sddl, fsacl)) - res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn), - attrs=["cn", "nTSecurityDescriptor"], - expression="", scope=ldb.SCOPE_ONELEVEL) + res = samdb.search(base="CN=Policies,CN=System,%s" %(domaindn), + attrs=["cn", "nTSecurityDescriptor"], + expression="", scope=ldb.SCOPE_ONELEVEL) for policy in res: acl = ndr_unpack(security.descriptor, - str(policy["nTSecurityDescriptor"])).as_sddl() + policy["nTSecurityDescriptor"][0]).as_sddl() policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"])) check_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp, domainsid, direct_db_access) def checksysvolacl(samdb, netlogon, sysvol, domainsid, dnsdomain, domaindn, - lp): + lp): """Set the ACL for the sysvol share and the subfolders :param samdb: An LDB object on the SAM db @@ -1799,12 +1850,12 @@ def checksysvolacl(samdb, netlogon, sysvol, domainsid, dnsdomain, domaindn, # Check acls on Policy folder and policies folders check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, - direct_db_access) + direct_db_access) -def interface_ips_v4(lp): +def interface_ips_v4(lp, all_interfaces=False): """return only IPv4 IPs""" - ips = samba.interface_ips(lp, False) + ips = samba.interface_ips(lp, all_interfaces) ret = [] for i in ips: if i.find(':') == -1: @@ -1831,7 +1882,8 @@ def provision_fill(samdb, secrets_ldb, logger, names, paths, invocationid=None, machinepass=None, ntdsguid=None, dns_backend=None, dnspass=None, serverrole=None, dom_for_fun_level=None, - am_rodc=False, lp=None, use_ntvfs=False, skip_sysvolacl=False): + am_rodc=False, lp=None, use_ntvfs=False, + skip_sysvolacl=False, backend_store=None): # create/adapt the group policy GUIDs # Default GUID for default policy are described at # "How Core Group Policy Works" @@ -1856,14 +1908,15 @@ def provision_fill(samdb, secrets_ldb, logger, names, paths, samdb.transaction_start() try: samdb = fill_samdb(samdb, lp, names, logger=logger, - schema=schema, - policyguid=policyguid, policyguid_dc=policyguid_dc, - fill=samdb_fill, adminpass=adminpass, krbtgtpass=krbtgtpass, - invocationid=invocationid, machinepass=machinepass, - dns_backend=dns_backend, dnspass=dnspass, - ntdsguid=ntdsguid, serverrole=serverrole, - dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc, - next_rid=next_rid, dc_rid=dc_rid) + schema=schema, + policyguid=policyguid, policyguid_dc=policyguid_dc, + fill=samdb_fill, adminpass=adminpass, krbtgtpass=krbtgtpass, + invocationid=invocationid, machinepass=machinepass, + dns_backend=dns_backend, dnspass=dnspass, + ntdsguid=ntdsguid, serverrole=serverrole, + dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc, + next_rid=next_rid, dc_rid=dc_rid, + backend_store=backend_store) # Set up group policies (domain policy and domain controller # policy) @@ -1887,9 +1940,9 @@ def provision_fill(samdb, secrets_ldb, logger, names, paths, logger.info("Setting acl on sysvol skipped") secretsdb_self_join(secrets_ldb, domain=names.domain, - realm=names.realm, dnsdomain=names.dnsdomain, - netbiosname=names.netbiosname, domainsid=names.domainsid, - machinepass=machinepass, secure_channel_type=SEC_CHAN_BDC) + realm=names.realm, dnsdomain=names.dnsdomain, + netbiosname=names.netbiosname, domainsid=names.domainsid, + machinepass=machinepass, secure_channel_type=SEC_CHAN_BDC) # Now set up the right msDS-SupportedEncryptionTypes into the DB # In future, this might be determined from some configuration @@ -1899,7 +1952,7 @@ def provision_fill(samdb, secrets_ldb, logger, names, paths, msg = ldb.Message(ldb.Dn(samdb, samdb.searchone("distinguishedName", expression="samAccountName=%s$" % names.netbiosname, - scope=ldb.SCOPE_SUBTREE))) + scope=ldb.SCOPE_SUBTREE).decode('utf8'))) msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement( elements=kerberos_enctypes, flags=ldb.FLAG_MOD_REPLACE, name="msDS-SupportedEncryptionTypes") @@ -1913,11 +1966,12 @@ def provision_fill(samdb, secrets_ldb, logger, names, paths, setup_ad_dns(samdb, secrets_ldb, names, paths, lp, logger, hostip=hostip, hostip6=hostip6, dns_backend=dns_backend, dnspass=dnspass, os_level=dom_for_fun_level, - targetdir=targetdir, fill_level=samdb_fill) + targetdir=targetdir, fill_level=samdb_fill, + backend_store=backend_store) domainguid = samdb.searchone(basedn=samdb.get_default_basedn(), - attribute="objectGUID") - assert isinstance(domainguid, str) + attribute="objectGUID").decode('utf8') + assert isinstance(domainguid, string_types) lastProvisionUSNs = get_last_provision_usn(samdb) maxUSN = get_max_usn(samdb, str(names.rootdn)) @@ -1928,12 +1982,12 @@ def provision_fill(samdb, secrets_ldb, logger, names, paths, logger.info("Setting up sam.ldb rootDSE marking as synchronized") setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"), - { 'NTDSGUID' : names.ntdsguid }) + {'NTDSGUID': names.ntdsguid}) # fix any dangling GUIDs from the provision logger.info("Fixing provision GUIDs") chk = dbcheck(samdb, samdb_schema=samdb, verbose=False, fix=True, yes=True, - quiet=True) + quiet=True) samdb.transaction_start() try: # a small number of GUIDs are missing because of ordering issues in the @@ -1950,7 +2004,7 @@ def provision_fill(samdb, secrets_ldb, logger, names, paths, 'ipsecNegotiationPolicyReference', 'ipsecNFAReference']) if chk.check_database(DN=names.schemadn, scope=ldb.SCOPE_SUBTREE, - attrs=['attributeId', 'governsId']) != 0: + attrs=['attributeId', 'governsId']) != 0: raise ProvisioningError("Duplicate attributeId or governsId in schema. Must be fixed manually!!") except: samdb.transaction_cancel() @@ -1971,7 +2025,7 @@ _ROLES_MAP = { "member server": "member server", "standalone": "standalone server", "standalone server": "standalone server", - } +} def sanitize_server_role(role): @@ -1989,7 +2043,7 @@ def sanitize_server_role(role): def provision_fake_ypserver(logger, samdb, domaindn, netbiosname, nisdomain, - maxuid, maxgid): + maxuid, maxgid): """Create AD entries for the fake ypserver. This is needed for being able to manipulate posix attrs via ADUC. @@ -1998,16 +2052,17 @@ def provision_fake_ypserver(logger, samdb, domaindn, netbiosname, nisdomain, try: logger.info("Setting up fake yp server settings") setup_add_ldif(samdb, setup_path("ypServ30.ldif"), { - "DOMAINDN": domaindn, - "NETBIOSNAME": netbiosname, - "NISDOMAIN": nisdomain, - }) + "DOMAINDN": domaindn, + "NETBIOSNAME": netbiosname, + "NISDOMAIN": nisdomain, + }) except: samdb.transaction_cancel() raise else: samdb.transaction_commit() + def directory_create_or_exists(path, mode=0o755): if not os.path.exists(path): try: @@ -2018,22 +2073,54 @@ def directory_create_or_exists(path, mode=0o755): else: raise ProvisioningError("Failed to create directory %s: %s" % (path, e.strerror)) + +def determine_host_ip(logger, lp, hostip=None): + if hostip is None: + logger.info("Looking up IPv4 addresses") + hostips = interface_ips_v4(lp) + if len(hostips) > 0: + hostip = hostips[0] + if len(hostips) > 1: + logger.warning("More than one IPv4 address found. Using %s", + hostip) + if hostip == "127.0.0.1": + hostip = None + if hostip is None: + logger.warning("No IPv4 address will be assigned") + + return hostip + + +def determine_host_ip6(logger, lp, hostip6=None): + if hostip6 is None: + logger.info("Looking up IPv6 addresses") + hostips = interface_ips_v6(lp) + if hostips: + hostip6 = hostips[0] + if len(hostips) > 1: + logger.warning("More than one IPv6 address found. Using %s", hostip6) + if hostip6 is None: + logger.warning("No IPv6 address will be assigned") + + return hostip6 + + def provision(logger, session_info, smbconf=None, - targetdir=None, samdb_fill=FILL_FULL, realm=None, rootdn=None, - domaindn=None, schemadn=None, configdn=None, serverdn=None, - domain=None, hostname=None, hostip=None, hostip6=None, domainsid=None, - next_rid=1000, dc_rid=None, adminpass=None, ldapadminpass=None, - krbtgtpass=None, domainguid=None, policyguid=None, policyguid_dc=None, - dns_backend=None, dns_forwarder=None, dnspass=None, - invocationid=None, machinepass=None, ntdsguid=None, - root=None, nobody=None, users=None, backup=None, aci=None, - serverrole=None, dom_for_fun_level=None, backend_type=None, - sitename=None, ol_mmr_urls=None, ol_olc=None, slapd_path=None, - useeadb=False, am_rodc=False, lp=None, use_ntvfs=False, - use_rfc2307=False, maxuid=None, maxgid=None, skip_sysvolacl=True, - ldap_backend_forced_uri=None, nosync=False, ldap_dryrun_mode=False, - ldap_backend_extra_port=None, base_schema=None, - plaintext_secrets=False): + targetdir=None, samdb_fill=FILL_FULL, realm=None, rootdn=None, + domaindn=None, schemadn=None, configdn=None, serverdn=None, + domain=None, hostname=None, hostip=None, hostip6=None, domainsid=None, + next_rid=1000, dc_rid=None, adminpass=None, ldapadminpass=None, + krbtgtpass=None, domainguid=None, policyguid=None, policyguid_dc=None, + dns_backend=None, dns_forwarder=None, dnspass=None, + invocationid=None, machinepass=None, ntdsguid=None, + root=None, nobody=None, users=None, backup=None, aci=None, + serverrole=None, dom_for_fun_level=None, backend_type=None, + sitename=None, ol_mmr_urls=None, ol_olc=None, slapd_path=None, + useeadb=False, am_rodc=False, lp=None, use_ntvfs=False, + use_rfc2307=False, maxuid=None, maxgid=None, skip_sysvolacl=True, + ldap_backend_forced_uri=None, nosync=False, ldap_dryrun_mode=False, + ldap_backend_extra_port=None, base_schema=None, + plaintext_secrets=False, backend_store=None): """Provision samba4 :note: caution, this wipes all existing data! @@ -2050,6 +2137,8 @@ def provision(logger, session_info, smbconf=None, if backend_type is None: backend_type = "ldb" + if backend_store is None: + backend_store = get_default_backend_store() if domainsid is None: domainsid = security.random_sid() @@ -2114,38 +2203,17 @@ def provision(logger, session_info, smbconf=None, lp = samba.param.LoadParm() lp.load(smbconf) names = guess_names(lp=lp, hostname=hostname, domain=domain, - dnsdomain=realm, serverrole=serverrole, domaindn=domaindn, - configdn=configdn, schemadn=schemadn, serverdn=serverdn, - sitename=sitename, rootdn=rootdn, domain_names_forced=(samdb_fill == FILL_DRS)) + dnsdomain=realm, serverrole=serverrole, domaindn=domaindn, + configdn=configdn, schemadn=schemadn, serverdn=serverdn, + sitename=sitename, rootdn=rootdn, domain_names_forced=(samdb_fill == FILL_DRS)) paths = provision_paths_from_lp(lp, names.dnsdomain) paths.bind_gid = bind_gid - paths.root_uid = root_uid; + 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) - if hostips: - hostip6 = hostips[0] - if len(hostips) > 1: - logger.warning("More than one IPv6 address found. Using %s", hostip6) - if hostip6 is None: - logger.warning("No IPv6 address will be assigned") - + hostip = determine_host_ip(logger, lp, hostip) + hostip6 = determine_host_ip6(logger, lp, hostip6) names.hostip = hostip names.hostip6 = hostip6 names.domainguid = domainguid @@ -2165,38 +2233,31 @@ def provision(logger, session_info, smbconf=None, if paths.sysvol and not os.path.exists(paths.sysvol): os.makedirs(paths.sysvol, 0o775) - ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="") + ldapi_url = "ldapi://%s" % urllib_quote(paths.s4_ldapi_path, safe="") schema = Schema(domainsid, invocationid=invocationid, - schemadn=names.schemadn, base_schema=base_schema) + schemadn=names.schemadn, base_schema=base_schema) if backend_type == "ldb": provision_backend = LDBBackend(backend_type, paths=paths, - lp=lp, - names=names, logger=logger) - elif backend_type == "existing": - # If support for this is ever added back, then the URI will need to be - # specified again - provision_backend = ExistingBackend(backend_type, paths=paths, - lp=lp, - names=names, logger=logger, - ldap_backend_forced_uri=ldap_backend_forced_uri) + lp=lp, + names=names, logger=logger) elif backend_type == "fedora-ds": provision_backend = FDSBackend(backend_type, paths=paths, - lp=lp, - names=names, logger=logger, domainsid=domainsid, - schema=schema, hostname=hostname, ldapadminpass=ldapadminpass, - slapd_path=slapd_path, - root=root) + lp=lp, + names=names, logger=logger, domainsid=domainsid, + schema=schema, hostname=hostname, ldapadminpass=ldapadminpass, + slapd_path=slapd_path, + root=root) elif backend_type == "openldap": provision_backend = OpenLDAPBackend(backend_type, paths=paths, - lp=lp, - names=names, logger=logger, domainsid=domainsid, - schema=schema, hostname=hostname, ldapadminpass=ldapadminpass, - slapd_path=slapd_path, ol_mmr_urls=ol_mmr_urls, - ldap_backend_extra_port=ldap_backend_extra_port, - ldap_dryrun_mode=ldap_dryrun_mode, nosync=nosync, - ldap_backend_forced_uri=ldap_backend_forced_uri) + lp=lp, + names=names, logger=logger, domainsid=domainsid, + schema=schema, hostname=hostname, ldapadminpass=ldapadminpass, + slapd_path=slapd_path, ol_mmr_urls=ol_mmr_urls, + ldap_backend_extra_port=ldap_backend_extra_port, + ldap_dryrun_mode=ldap_dryrun_mode, nosync=nosync, + ldap_backend_forced_uri=ldap_backend_forced_uri) else: raise ValueError("Unknown LDAP backend type selected") @@ -2211,8 +2272,8 @@ def provision(logger, session_info, smbconf=None, logger.info("Setting up secrets.ldb") secrets_ldb = setup_secretsdb(paths, - session_info=session_info, - backend_credentials=provision_backend.credentials, lp=lp) + session_info=session_info, + backend_credentials=provision_backend.credentials, lp=lp) try: logger.info("Setting up the registry") @@ -2233,7 +2294,8 @@ def provision(logger, session_info, smbconf=None, provision_backend, lp, names, logger=logger, serverrole=serverrole, schema=schema, fill=samdb_fill, am_rodc=am_rodc, - plaintext_secrets=plaintext_secrets) + plaintext_secrets=plaintext_secrets, + backend_store=backend_store) if serverrole == "active directory domain controller": if paths.netlogon is None: @@ -2249,22 +2311,24 @@ def provision(logger, session_info, smbconf=None, adminpass = samba.generate_random_password(12, 32) adminpass_generated = True else: - adminpass = unicode(adminpass, 'utf-8') + if isinstance(adminpass, binary_type): + adminpass = adminpass.decode('utf-8') adminpass_generated = False if samdb_fill == FILL_FULL: provision_fill(samdb, secrets_ldb, logger, names, paths, - schema=schema, targetdir=targetdir, samdb_fill=samdb_fill, - hostip=hostip, hostip6=hostip6, - next_rid=next_rid, dc_rid=dc_rid, adminpass=adminpass, - krbtgtpass=krbtgtpass, - policyguid=policyguid, policyguid_dc=policyguid_dc, - invocationid=invocationid, machinepass=machinepass, - ntdsguid=ntdsguid, dns_backend=dns_backend, - dnspass=dnspass, serverrole=serverrole, - dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc, - lp=lp, use_ntvfs=use_ntvfs, - skip_sysvolacl=skip_sysvolacl) + schema=schema, targetdir=targetdir, samdb_fill=samdb_fill, + hostip=hostip, hostip6=hostip6, + next_rid=next_rid, dc_rid=dc_rid, adminpass=adminpass, + krbtgtpass=krbtgtpass, + policyguid=policyguid, policyguid_dc=policyguid_dc, + invocationid=invocationid, machinepass=machinepass, + ntdsguid=ntdsguid, dns_backend=dns_backend, + dnspass=dnspass, serverrole=serverrole, + dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc, + lp=lp, use_ntvfs=use_ntvfs, + skip_sysvolacl=skip_sysvolacl, + backend_store=backend_store) if not is_heimdal_built(): create_kdc_conf(paths.kdcconf, realm, domain, os.path.dirname(lp.get("log file"))) @@ -2318,7 +2382,7 @@ def provision(logger, session_info, smbconf=None, os.chmod(paths.binddns_dir, 0o770) os.chown(paths.binddns_dir, -1, paths.bind_gid) except OSError: - if not os.environ.has_key('SAMBA_SELFTEST'): + if 'SAMBA_SELFTEST' not in os.environ: logger.info("Failed to chown %s to bind gid %u", paths.binddns_dir, paths.bind_gid) @@ -2326,7 +2390,7 @@ def provision(logger, session_info, smbconf=None, os.chmod(bind_dns_keytab_path, 0o640) os.chown(bind_dns_keytab_path, -1, paths.bind_gid) except OSError: - if not os.environ.has_key('SAMBA_SELFTEST'): + if 'SAMBA_SELFTEST' not in os.environ: logger.info("Failed to chown %s to bind gid %u", bind_dns_keytab_path, paths.bind_gid) @@ -2351,33 +2415,33 @@ def provision(logger, session_info, smbconf=None, if use_rfc2307: provision_fake_ypserver(logger=logger, samdb=samdb, - domaindn=names.domaindn, netbiosname=names.netbiosname, - nisdomain=names.domain.lower(), maxuid=maxuid, maxgid=maxgid) + domaindn=names.domaindn, netbiosname=names.netbiosname, + nisdomain=names.domain.lower(), maxuid=maxuid, maxgid=maxgid) return result def provision_become_dc(smbconf=None, targetdir=None, - realm=None, rootdn=None, domaindn=None, schemadn=None, configdn=None, - serverdn=None, domain=None, hostname=None, domainsid=None, - adminpass=None, krbtgtpass=None, domainguid=None, policyguid=None, - policyguid_dc=None, invocationid=None, machinepass=None, dnspass=None, - dns_backend=None, root=None, nobody=None, users=None, - backup=None, serverrole=None, ldap_backend=None, - ldap_backend_type=None, sitename=None, debuglevel=1, use_ntvfs=False): + realm=None, rootdn=None, domaindn=None, schemadn=None, configdn=None, + serverdn=None, domain=None, hostname=None, domainsid=None, + adminpass=None, krbtgtpass=None, domainguid=None, policyguid=None, + policyguid_dc=None, invocationid=None, machinepass=None, dnspass=None, + dns_backend=None, root=None, nobody=None, users=None, + backup=None, serverrole=None, ldap_backend=None, + ldap_backend_type=None, sitename=None, debuglevel=1, use_ntvfs=False): logger = logging.getLogger("provision") samba.set_debug_level(debuglevel) res = provision(logger, system_session(), - smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS, - realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn, - configdn=configdn, serverdn=serverdn, domain=domain, - hostname=hostname, hostip=None, domainsid=domainsid, - machinepass=machinepass, - serverrole="active directory domain controller", - sitename=sitename, dns_backend=dns_backend, dnspass=dnspass, - use_ntvfs=use_ntvfs) + smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS, + realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn, + configdn=configdn, serverdn=serverdn, domain=domain, + hostname=hostname, hostip=None, domainsid=domainsid, + machinepass=machinepass, + serverrole="active directory domain controller", + sitename=sitename, dns_backend=dns_backend, dnspass=dnspass, + use_ntvfs=use_ntvfs) res.lp.set("debuglevel", str(debuglevel)) return res @@ -2394,7 +2458,7 @@ def create_krb5_conf(path, dnsdomain, hostname, realm): "DNSDOMAIN": dnsdomain, "HOSTNAME": hostname, "REALM": realm, - }) + }) class ProvisioningError(Exception):