1
0
mirror of https://github.com/samba-team/samba.git synced 2025-11-25 00:23:52 +03:00

python/provision: Reconcile code partitions-only provisioning and generic provisioning, some other minor refactoring of the provisioning.

Pair-programmed by Andrew and me using obby :-)
This commit is contained in:
Jelmer Vernooij
2008-01-25 01:02:13 +01:00
parent 59bb26772f
commit 688adcbb63
6 changed files with 314 additions and 251 deletions

View File

@@ -1,8 +1,10 @@
#!/usr/bin/python #!/usr/bin/python
# Unix SMB/CIFS implementation. # Unix SMB/CIFS implementation.
# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2008
#
# Based on the original in EJS:
# Copyright (C) Andrew Tridgell <tridge@samba.org> 2005 # Copyright (C) Andrew Tridgell <tridge@samba.org> 2005
# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007
# #
# This program is free software; you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@@ -204,7 +206,7 @@ def check_all_substituted(text):
var_start = text.find("${") var_start = text.find("${")
var_end = text.find("}", var_start) var_end = text.find("}", var_start)
raise Exception("Not all variables substituted: %s" % text[var_start:var_end]) raise Exception("Not all variables substituted: %s" % text[var_start:var_end+1])
def valid_netbios_name(name): def valid_netbios_name(name):

View File

@@ -1,10 +1,25 @@
# #
# backend code for provisioning a Samba4 server # Unix SMB/CIFS implementation.
# Released under the GNU GPL v3 or later # backend code for provisioning a Samba4 server
# Copyright Jelmer Vernooij 2007
# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2008
# Copyright (C) Andrew Bartlett <abartlet@samba.org> 2008
# #
# Based on the original in EJS: # Based on the original in EJS:
# Copyright Andrew Tridgell 2005 # 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/>.
# #
from base64 import b64encode from base64 import b64encode
@@ -17,9 +32,10 @@ from socket import gethostname, gethostbyname
import param import param
import registry import registry
import samba import samba
from samba import Ldb, substitute_var, valid_netbios_name from samba import Ldb, substitute_var, valid_netbios_name, check_all_substituted
from samba.samdb import SamDB from samba.samdb import SamDB
import security import security
import urllib
from ldb import SCOPE_SUBTREE, SCOPE_ONELEVEL, SCOPE_BASE, LdbError, \ from ldb import SCOPE_SUBTREE, SCOPE_ONELEVEL, SCOPE_BASE, LdbError, \
LDB_ERR_NO_SUCH_OBJECT, timestring, CHANGETYPE_MODIFY, CHANGETYPE_NONE LDB_ERR_NO_SUCH_OBJECT, timestring, CHANGETYPE_MODIFY, CHANGETYPE_NONE
@@ -53,7 +69,7 @@ class ProvisionPaths:
self.ldap_schema_basedn_ldif = None self.ldap_schema_basedn_ldif = None
def install_ok(lp, session_info, credentials): def check_install(lp, session_info, credentials):
"""Check whether the current install seems ok. """Check whether the current install seems ok.
:param lp: Loadparm context :param lp: Loadparm context
@@ -61,12 +77,11 @@ def install_ok(lp, session_info, credentials):
:param credentials: Credentials :param credentials: Credentials
""" """
if lp.get("realm") == "": if lp.get("realm") == "":
return False raise Error("Realm empty")
ldb = Ldb(lp.get("sam database"), session_info=session_info, ldb = Ldb(lp.get("sam database"), session_info=session_info,
credentials=credentials, lp=lp) credentials=credentials, lp=lp)
if len(ldb.search("(cn=Administrator)")) != 1: if len(ldb.search("(cn=Administrator)")) != 1:
return False raise "No administrator account found"
return True
def findnss(nssfn, *names): def findnss(nssfn, *names):
@@ -112,7 +127,7 @@ def setup_add_ldif(ldb, ldif_path, subst_vars=None):
if subst_vars is not None: if subst_vars is not None:
data = substitute_var(data, subst_vars) data = substitute_var(data, subst_vars)
assert "${" not in data check_all_substituted(data)
ldb.add_ldif(data) ldb.add_ldif(data)
@@ -128,7 +143,7 @@ def setup_modify_ldif(ldb, ldif_path, substvars=None):
if substvars is not None: if substvars is not None:
data = substitute_var(data, substvars) data = substitute_var(data, substvars)
assert "${" not in data check_all_substituted(data)
ldb.modify_ldif(data) ldb.modify_ldif(data)
@@ -159,19 +174,20 @@ def setup_file(template, fname, substvars):
data = open(template, 'r').read() data = open(template, 'r').read()
if substvars: if substvars:
data = substitute_var(data, substvars) data = substitute_var(data, substvars)
assert not "${" in data check_all_substituted(data)
open(f, 'w').write(data) open(f, 'w').write(data)
def provision_paths_from_lp(lp, dnsdomain): def provision_paths_from_lp(lp, dnsdomain, private_dir=None):
"""Set the default paths for provisioning. """Set the default paths for provisioning.
:param lp: Loadparm context. :param lp: Loadparm context.
:param dnsdomain: DNS Domain name :param dnsdomain: DNS Domain name
""" """
paths = ProvisionPaths() paths = ProvisionPaths()
private_dir = lp.get("private dir") if private_dir is None:
private_dir = lp.get("private dir")
paths.shareconf = os.path.join(private_dir, "share.ldb") paths.shareconf = os.path.join(private_dir, "share.ldb")
paths.samdb = os.path.join(private_dir, lp.get("sam database") or "samdb.ldb") paths.samdb = os.path.join(private_dir, lp.get("sam database") or "samdb.ldb")
paths.secrets = os.path.join(private_dir, lp.get("secrets database") or "secrets.ldb") paths.secrets = os.path.join(private_dir, lp.get("secrets database") or "secrets.ldb")
@@ -235,57 +251,144 @@ def setup_name_mappings(ldb, sid, domaindn, root, nobody, nogroup, users,
ldb.setup_name_mapping(domaindn, sid + "-520", wheel) ldb.setup_name_mapping(domaindn, sid + "-520", wheel)
def provision_become_dc(setup_dir, message, paths, lp, session_info, def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
credentials): credentials, configdn, schemadn, domaindn,
hostname, netbiosname, dnsdomain, realm,
rootdn, serverrole, ldap_backend=None,
ldap_backend_type=None, erase=False):
"""Setup the partitions for the SAM database.
Alternatively, provision() may call this, and then populate the database.
:param erase: Remove the existing data present in the database.
:param
: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 assert session_info is not None
erase = False
def setup_path(file): if os.path.exists(samdb_path):
return os.path.join(setup_dir, file) os.unlink(samdb_path)
os.path.unlink(paths.samdb)
message("Setting up templates db")
setup_templatesdb(paths.templates, setup_path, session_info=session_info,
credentials=credentials, lp=lp)
# Also wipes the database # Also wipes the database
message("Setting up sam.ldb") samdb = SamDB(samdb_path, session_info=session_info,
samdb = SamDB(paths.samdb, session_info=session_info,
credentials=credentials, lp=lp) credentials=credentials, lp=lp)
message("Setting up sam.ldb partitions") #Add modules to the list to activate them by default
setup_samdb_partitions(samdb, setup_path, schemadn, configdn, domaindn) #beware often order is important
#
# Some Known ordering constraints:
# - rootdse must be first, as it makes redirects from "" -> cn=rootdse
# - objectclass must be before password_hash, because password_hash checks
# that the objectclass is of type person (filled in by objectclass
# module when expanding the objectclass list)
# - partition must be last
# - each partition has its own module list then
modules_list = ["rootdse",
"paged_results",
"ranged_results",
"anr",
"server_sort",
"extended_dn",
"asq",
"samldb",
"rdn_name",
"objectclass",
"kludge_acl",
"operational"]
tdb_modules_list = [
"subtree_rename",
"subtree_delete",
"linked_attributes"]
modules_list2 = ["show_deleted",
"partition"]
domaindn_ldb = "users.ldb"
if ldap_backend is not None:
domaindn_ldb = ldap_backend
configdn_ldb = "configuration.ldb"
if ldap_backend is not None:
configdn_ldb = ldap_backend
schema_ldb = "schema.ldb"
if ldap_backend is not None:
schema_ldb = ldap_backend
if ldap_backend_type == "fedora-ds":
backend_modules = ["nsuniqueid","paged_searches"]
elif ldap_backend_type == "openldap":
backend_modules = ["normalise","entryuuid","paged_searches"]
elif serverrole == "domain controller":
backend_modules = ["repl_meta_data"]
else:
backend_modules = ["objectguid"]
setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
"SCHEMADN": schemadn,
"SCHEMADN_LDB": "schema.ldb",
"SCHEMADN_MOD2": ",objectguid",
"CONFIGDN": configdn,
"CONFIGDN_LDB": "configuration.ldb",
"DOMAINDN": domaindn,
"DOMAINDN_LDB": "users.ldb",
"SCHEMADN_MOD": "schema_fsmo,instancetype",
"CONFIGDN_MOD": "naming_fsmo,instancetype",
"DOMAINDN_MOD": "pdc_fsmo,password_hash,instancetype",
"MODULES_LIST": ",".join(modules_list),
"TDB_MODULES_LIST": ","+",".join(tdb_modules_list),
"MODULES_LIST2": ",".join(modules_list2),
"BACKEND_MOD": ",".join(backend_modules),
})
samdb = SamDB(paths.samdb, session_info=session_info, samdb = SamDB(samdb_path, session_info=session_info,
credentials=credentials, lp=lp) credentials=credentials, lp=lp)
ldb.transaction_start() samdb.transaction_start()
try: try:
message("Setting up sam.ldb attributes") message("Setting up sam.ldb attributes")
samdb.load_ldif_file_add(setup_path("provision_init.ldif")) samdb.load_ldif_file_add(setup_path("provision_init.ldif"))
message("Setting up sam.ldb rootDSE") message("Setting up sam.ldb rootDSE")
setup_samdb_rootdse(samdb, setup_path, schemadn, domaindn, setup_samdb_rootdse(samdb, setup_path, schemadn, domaindn, hostname,
hostname, dnsdomain, realm, rootdn, configdn, dnsdomain, realm, rootdn, configdn, netbiosname)
netbiosname)
if erase: if erase:
message("Erasing data from partitions") message("Erasing data from partitions")
samdb.erase_partitions() samdb.erase_partitions()
message("Setting up sam.ldb indexes")
samdb.load_ldif_file_add(setup_path("provision_index.ldif"))
except: except:
samdb.transaction_cancel() samdb.transaction_cancel()
raise raise
samdb.transaction_commit() samdb.transaction_commit()
return samdb
message("Setting up %s" % paths.secrets)
secrets_ldb = setup_secretsdb(paths.secrets, setup_path, session_info, def secretsdb_become_dc(secretsdb, setup_path, domain, realm, dnsdomain,
credentials, lp) netbiosname, domainsid, keytab_path, samdb_url,
setup_ldb(secrets_ldb, setup_path("secrets_dc.ldif"), dns_keytab_path, dnspass, machinepass):
{ "MACHINEPASS_B64": b64encode(machinepass) }) """Add DC-specific bits to a secrets database.
:param secretsdb: Ldb Handle to the secrets database
:param setup_path: Setup path function
:param machinepass: Machine password
"""
setup_ldb(secretsdb, setup_path("secrets_dc.ldif"), {
"MACHINEPASS_B64": b64encode(machinepass),
"DOMAIN": domain,
"REALM": realm,
"DNSDOMAIN": dnsdomain,
"DOMAINSID": str(domainsid),
"SECRETS_KEYTAB": keytab_path,
"NETBIOSNAME": netbiosname,
"SAM_LDB": samdb_url,
"DNS_KEYTAB": dns_keytab_path,
"DNSPASS_B64": b64encode(dnspass),
})
def setup_secretsdb(path, setup_path, session_info, credentials, lp): def setup_secretsdb(path, setup_path, session_info, credentials, lp):
@@ -296,10 +399,12 @@ def setup_secretsdb(path, setup_path, session_info, credentials, lp):
:param session_info: Session info. :param session_info: Session info.
:param credentials: Credentials :param credentials: Credentials
:param lp: Loadparm context :param lp: Loadparm context
:return: LDB handle for the created secrets database
""" """
if os.path.exists(path): if os.path.exists(path):
os.unlink(path) os.unlink(path)
secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials, lp=lp) secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
lp=lp)
secrets_ldb.erase() secrets_ldb.erase()
secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif")) secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif")) secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
@@ -316,7 +421,7 @@ def setup_templatesdb(path, setup_path, session_info, credentials, lp):
:param lp: Loadparm context :param lp: Loadparm context
""" """
templates_ldb = SamDB(path, session_info=session_info, templates_ldb = SamDB(path, session_info=session_info,
credentials=credentials, lp=lp) credentials=credentials, lp=lp)
templates_ldb.erase() templates_ldb.erase()
templates_ldb.load_ldif_file_add(setup_path("provision_templates.ldif")) templates_ldb.load_ldif_file_add(setup_path("provision_templates.ldif"))
@@ -359,69 +464,13 @@ def setup_samdb_rootdse(samdb, setup_path, schemadn, domaindn, hostname,
"CONFIGDN": configdn, "CONFIGDN": configdn,
"VERSION": samba.version(), "VERSION": samba.version(),
}) })
def setup_samdb_partitions(samdb, setup_path, schemadn, configdn, domaindn):
"""Setup SAM database partitions.
:param samdb: Sam Database handle
:param setup_path: Setup path function
:param schemadn: Schema DN.
:param configdn: Configuration DN.
:param domaindn: Domain DN.
"""
#Add modules to the list to activate them by default
#beware often order is important
#
# Some Known ordering constraints:
# - rootdse must be first, as it makes redirects from "" -> cn=rootdse
# - objectclass must be before password_hash, because password_hash checks
# that the objectclass is of type person (filled in by objectclass
# module when expanding the objectclass list)
# - partition must be last
# - each partition has its own module list then
modules_list = ["rootdse",
"paged_results",
"ranged_results",
"anr",
"server_sort",
"extended_dn",
"asq",
"samldb",
"rdn_name",
"objectclass",
"kludge_acl",
"operational"]
tdb_modules_list = [
"subtree_rename",
"subtree_delete",
"linked_attributes"]
modules_list2 = ["show_deleted",
"partition"]
setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
"SCHEMADN": schemadn,
"SCHEMADN_LDB": "schema.ldb",
"SCHEMADN_MOD2": ",objectguid",
"CONFIGDN": configdn,
"CONFIGDN_LDB": "configuration.ldb",
"DOMAINDN": domaindn,
"DOMAINDN_LDB": "users.ldb",
"SCHEMADN_MOD": "schema_fsmo",
"CONFIGDN_MOD": "naming_fsmo",
"CONFIGDN_MOD2": ",objectguid",
"DOMAINDN_MOD": "pdc_fsmo,password_hash",
"DOMAINDN_MOD2": ",objectguid",
"MODULES_LIST": ",".join(modules_list),
"TDB_MODULES_LIST": ","+",".join(tdb_modules_list),
"MODULES_LIST2": ",".join(modules_list2),
})
def setup_self_join(samdb, configdn, schemadn, domaindn, def setup_self_join(samdb, configdn, schemadn, domaindn,
netbiosname, hostname, dnsdomain, machinepass, dnspass, netbiosname, hostname, dnsdomain, machinepass, dnspass,
realm, domainname, domainsid, invocationid, setup_path, realm, domainname, domainsid, invocationid, setup_path,
policyguid, hostguid=None): policyguid, hostguid=None):
"""Join a host to its own domain."""
if hostguid is not None: if hostguid is not None:
hostguid_add = "objectGUID: %s" % hostguid hostguid_add = "objectGUID: %s" % hostguid
else: else:
@@ -451,43 +500,39 @@ def setup_self_join(samdb, configdn, schemadn, domaindn,
def setup_samdb(path, setup_path, session_info, credentials, lp, def setup_samdb(path, setup_path, session_info, credentials, lp,
schemadn, configdn, domaindn, dnsdomain, realm, schemadn, configdn, domaindn, dnsdomain, realm,
netbiosname, message, hostname, rootdn, erase, netbiosname, message, hostname, rootdn, erase,
domainsid, aci, rdn_dc, domainguid, policyguid, domainsid, aci, domainguid, policyguid,
domainname, blank, adminpass, krbtgtpass, domainname, fill, adminpass, krbtgtpass,
machinepass, hostguid, invocationid, dnspass): machinepass, hostguid, invocationid, dnspass,
serverrole, ldap_backend=None, ldap_backend_type=None):
"""Setup a complete SAM Database.
"""
# Also wipes the database # Also wipes the database
message("Setting up sam.ldb") setup_samdb_partitions(path, setup_path, schemadn=schemadn, configdn=configdn,
samdb = SamDB(path, session_info=session_info, domaindn=domaindn, message=message, lp=lp,
credentials=credentials, lp=lp) credentials=credentials, session_info=session_info,
hostname=hostname, netbiosname=netbiosname,
message("Setting up sam.ldb partitions") dnsdomain=dnsdomain, realm=realm, rootdn=rootdn,
setup_samdb_partitions(samdb, setup_path, schemadn, configdn, domaindn) ldap_backend=ldap_backend, serverrole=serverrole,
ldap_backend_type=ldap_backend_type, erase=erase)
samdb = SamDB(path, session_info=session_info, samdb = SamDB(path, session_info=session_info,
credentials=credentials, lp=lp) credentials=credentials, lp=lp)
samdb.transaction_start() if fill == FILL_DRS:
try: # We want to finish here, but setup the index before we do so
message("Setting up sam.ldb attributes") message("Setting up sam.ldb index")
samdb.load_ldif_file_add(setup_path("provision_init.ldif")) samdb.load_ldif_file_add(setup_path("provision_index.ldif"))
return samdb
message("Setting up sam.ldb rootDSE")
setup_samdb_rootdse(samdb, setup_path, schemadn, domaindn,
hostname, dnsdomain, realm, rootdn, configdn,
netbiosname)
if erase:
message("Erasing data from partitions")
samdb.erase_partitions()
except:
samdb.transaction_cancel()
raise
samdb.transaction_commit()
message("Pre-loading the Samba 4 and AD schema") message("Pre-loading the Samba 4 and AD schema")
samdb = SamDB(path, session_info=session_info, samdb = SamDB(path, session_info=session_info,
credentials=credentials, lp=lp) credentials=credentials, lp=lp)
samdb.set_domain_sid(domainsid) samdb.set_domain_sid(domainsid)
if lp.get("server role") == "domain controller":
samdb.set_invocation_id(invocationid)
load_schema(setup_path, samdb, schemadn, netbiosname, configdn) load_schema(setup_path, samdb, schemadn, netbiosname, configdn)
samdb.transaction_start() samdb.transaction_start()
@@ -497,7 +542,6 @@ def setup_samdb(path, setup_path, session_info, credentials, lp,
setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), { setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
"DOMAINDN": domaindn, "DOMAINDN": domaindn,
"ACI": aci, "ACI": aci,
"RDN_DC": rdn_dc,
}) })
message("Modifying DomainDN: " + domaindn + "") message("Modifying DomainDN: " + domaindn + "")
@@ -507,7 +551,6 @@ def setup_samdb(path, setup_path, session_info, credentials, lp,
domainguid_mod = "" domainguid_mod = ""
setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), { setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
"RDN_DC": rdn_dc,
"LDAPTIME": timestring(int(time.time())), "LDAPTIME": timestring(int(time.time())),
"DOMAINSID": str(domainsid), "DOMAINSID": str(domainsid),
"SCHEMADN": schemadn, "SCHEMADN": schemadn,
@@ -538,7 +581,8 @@ def setup_samdb(path, setup_path, session_info, credentials, lp,
"EXTENSIBLEOBJECT": "# no objectClass: extensibleObject for local ldb" "EXTENSIBLEOBJECT": "# no objectClass: extensibleObject for local ldb"
}) })
message("Modifying schema container") message("Modifying schema container")
setup_modify_ldif(samdb, setup_path("provision_schema_basedn_modify.ldif"), { setup_modify_ldif(samdb,
setup_path("provision_schema_basedn_modify.ldif"), {
"SCHEMADN": schemadn, "SCHEMADN": schemadn,
"NETBIOSNAME": netbiosname, "NETBIOSNAME": netbiosname,
"DEFAULTSITE": DEFAULTSITE, "DEFAULTSITE": DEFAULTSITE,
@@ -587,7 +631,7 @@ def setup_samdb(path, setup_path, session_info, credentials, lp,
"CONFIGDN": configdn, "CONFIGDN": configdn,
}) })
if not blank: if fill == FILL_FULL:
message("Setting up sam.ldb users and groups") message("Setting up sam.ldb users and groups")
setup_add_ldif(samdb, setup_path("provision_users.ldif"), { setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
"DOMAINDN": domaindn, "DOMAINDN": domaindn,
@@ -608,11 +652,9 @@ def setup_samdb(path, setup_path, session_info, credentials, lp,
hostname=hostname, hostguid=hostguid, hostname=hostname, hostguid=hostguid,
setup_path=setup_path) setup_path=setup_path)
#We want to setup the index last, as adds are faster unindexed
message("Setting up sam.ldb index") message("Setting up sam.ldb index")
samdb.load_ldif_file_add(setup_path("provision_index.ldif")) samdb.load_ldif_file_add(setup_path("provision_index.ldif"))
message("Setting up sam.ldb rootDSE marking as synchronized")
setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
except: except:
samdb.transaction_cancel() samdb.transaction_cancel()
raise raise
@@ -620,14 +662,18 @@ def setup_samdb(path, setup_path, session_info, credentials, lp,
samdb.transaction_commit() samdb.transaction_commit()
return samdb return samdb
FILL_FULL = "FULL"
FILL_NT4SYNC = "NT4SYNC"
FILL_DRS = "DRS"
def provision(lp, setup_dir, message, blank, paths, session_info, def provision(lp, setup_dir, message, paths, session_info,
credentials, ldapbackend, realm=None, domain=None, hostname=None, credentials, ldapbackend, samdb_fill=FILL_FULL, realm=None, rootdn=None,
hostip=None, domainsid=None, hostguid=None, adminpass=None, domain=None, hostname=None, hostip=None, domainsid=None,
krbtgtpass=None, domainguid=None, policyguid=None, hostguid=None, adminpass=None, krbtgtpass=None, domainguid=None,
invocationid=None, machinepass=None, dnspass=None, root=None, policyguid=None, invocationid=None, machinepass=None,
nobody=None, nogroup=None, users=None, wheel=None, backup=None, dnspass=None, root=None, nobody=None, nogroup=None, users=None,
aci=None, serverrole=None): wheel=None, backup=None, aci=None, serverrole=None, erase=False,
ldap_backend=None, ldap_backend_type=None):
"""Provision samba4 """Provision samba4
:note: caution, this wipes all existing data! :note: caution, this wipes all existing data!
@@ -636,14 +682,10 @@ def provision(lp, setup_dir, message, blank, paths, session_info,
def setup_path(file): def setup_path(file):
return os.path.join(setup_dir, file) return os.path.join(setup_dir, file)
erase = False
if domainsid is None: if domainsid is None:
domainsid = security.random_sid() domainsid = security.random_sid()
if policyguid is None: if policyguid is None:
policyguid = uuid.random() policyguid = uuid.random()
if invocationid is None:
invocationid = uuid.random()
if adminpass is None: if adminpass is None:
adminpass = misc.random_password(12) adminpass = misc.random_password(12)
if krbtgtpass is None: if krbtgtpass is None:
@@ -669,29 +711,25 @@ def provision(lp, setup_dir, message, blank, paths, session_info,
aci = "# no aci for local ldb" aci = "# no aci for local ldb"
if serverrole is None: if serverrole is None:
serverrole = lp.get("server role") serverrole = lp.get("server role")
if invocationid is None and serverrole == "domain controller":
invocationid = uuid.random()
if realm is None: if realm is None:
realm = lp.get("realm") realm = lp.get("realm")
else:
if lp.get("realm").upper() != realm.upper(): if lp.get("realm").upper() != realm.upper():
raise Exception("realm '%s' in smb.conf must match chosen realm '%s'\n" % raise Exception("realm '%s' in smb.conf must match chosen realm '%s'" %
(lp.get("realm"), realm)) (lp.get("realm"), realm))
ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path)
if ldap_backend == "ldapi":
# provision-backend will set this path suggested slapd command line / fedorads.inf
ldap_backend = "ldapi://" % urllib.quote(os.path.join(lp.get("private dir"), "ldap", "ldapi"))
assert realm is not None assert realm is not None
realm = realm.upper() realm = realm.upper()
if domain is None:
domain = lp.get("workgroup")
else:
if lp.get("workgroup").upper() != domain.upper():
raise Error("workgroup '%s' in smb.conf must match chosen domain '%s'\n",
lp.get("workgroup"), domain)
assert domain is not None
domain = domain.upper()
if not valid_netbios_name(domain):
raise InvalidNetbiosName(domain)
if hostname is None: if hostname is None:
hostname = gethostname().split(".")[0].lower() hostname = gethostname().split(".")[0].lower()
@@ -703,13 +741,30 @@ def provision(lp, setup_dir, message, blank, paths, session_info,
raise InvalidNetbiosName(netbiosname) raise InvalidNetbiosName(netbiosname)
dnsdomain = realm.lower() dnsdomain = realm.lower()
domaindn = "DC=" + dnsdomain.replace(".", ",DC=") if serverrole == "domain controller":
rootdn = domaindn domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
if domain is None:
domain = lp.get("workgroup")
if lp.get("workgroup").upper() != domain.upper():
raise Error("workgroup '%s' in smb.conf must match chosen domain '%s'",
lp.get("workgroup"), domain)
assert domain is not None
domain = domain.upper()
if not valid_netbios_name(domain):
raise InvalidNetbiosName(domain)
else:
domaindn = "CN=" + netbiosname
domain = netbiosname
if rootdn is None:
rootdn = domaindn
configdn = "CN=Configuration," + rootdn configdn = "CN=Configuration," + rootdn
schemadn = "CN=Schema," + configdn schemadn = "CN=Schema," + configdn
rdn_dc = domaindn.split(",")[0][len("DC="):]
message("set DOMAIN SID: %s" % str(domainsid)) message("set DOMAIN SID: %s" % str(domainsid))
message("Provisioning for %s in realm %s" % (domain, realm)) message("Provisioning for %s in realm %s" % (domain, realm))
message("Using administrator password: %s" % adminpass) message("Using administrator password: %s" % adminpass)
@@ -725,7 +780,8 @@ def provision(lp, setup_dir, message, blank, paths, session_info,
smbconfsuffix = "member" smbconfsuffix = "member"
else: else:
assert "Invalid server role setting: %s" % serverrole assert "Invalid server role setting: %s" % serverrole
setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix), paths.smbconf, { setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
paths.smbconf, {
"HOSTNAME": hostname, "HOSTNAME": hostname,
"DOMAIN_CONF": domain, "DOMAIN_CONF": domain,
"REALM_CONF": realm, "REALM_CONF": realm,
@@ -742,6 +798,7 @@ def provision(lp, setup_dir, message, blank, paths, session_info,
credentials=credentials, lp=lp) credentials=credentials, lp=lp)
share_ldb.load_ldif_file_add(setup_path("share.ldif")) share_ldb.load_ldif_file_add(setup_path("share.ldif"))
message("Setting up secrets.ldb") message("Setting up secrets.ldb")
secrets_ldb = setup_secretsdb(paths.secrets, setup_path, secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
session_info=session_info, session_info=session_info,
@@ -755,44 +812,47 @@ def provision(lp, setup_dir, message, blank, paths, session_info,
setup_templatesdb(paths.templates, setup_path, session_info=session_info, setup_templatesdb(paths.templates, setup_path, session_info=session_info,
credentials=credentials, lp=lp) credentials=credentials, lp=lp)
samdb = setup_samdb(paths.samdb, setup_path, session_info=session_info, credentials=credentials, samdb = setup_samdb(paths.samdb, setup_path, session_info=session_info,
lp=lp, schemadn=schemadn, configdn=configdn, domaindn=domaindn, credentials=credentials, lp=lp, schemadn=schemadn,
dnsdomain=dnsdomain, netbiosname=netbiosname, realm=realm, message=message, configdn=configdn, domaindn=domaindn,
hostname=hostname, rootdn=rootdn, erase=erase, domainsid=domainsid, aci=aci, dnsdomain=dnsdomain, netbiosname=netbiosname,
rdn_dc=rdn_dc, domainguid=domainguid, policyguid=policyguid, realm=realm, message=message, hostname=hostname,
domainname=domain, blank=blank, adminpass=adminpass, krbtgtpass=krbtgtpass, rootdn=rootdn, erase=erase, domainsid=domainsid,
hostguid=hostguid, invocationid=invocationid, machinepass=machinepass, aci=aci, domainguid=domainguid, policyguid=policyguid,
dnspass=dnspass) domainname=domain, fill=samdb_fill,
adminpass=adminpass, krbtgtpass=krbtgtpass,
hostguid=hostguid, invocationid=invocationid,
machinepass=machinepass, dnspass=dnspass,
serverrole=serverrole, ldap_backend=ldap_backend,
ldap_backend_type=ldap_backend_type)
if lp.get("server role") == "domain controller": if lp.get("server role") == "domain controller":
os.makedirs(os.path.join(paths.sysvol, dnsdomain, "Policies", "{" + policyguid + "}"), 0755) policy_path = os.path.join(paths.sysvol, dnsdomain, "Policies",
os.makedirs(os.path.join(paths.sysvol, dnsdomain, "Policies", "{" + policyguid + "}", "Machine"), 0755) "{" + policyguid + "}")
os.makedirs(os.path.join(paths.sysvol, dnsdomain, "Policies", "{" + policyguid + "}", "User"), 0755) os.makedirs(policy_path, 0755)
if not os.path.isdir(paths.netlogon): os.makedirs(os.path.join(policy_path, "Machine"), 0755)
os.makedirs(os.path.join(policy_path, "User"), 0755)
if not os.path.isdir(paths.netlogon):
os.makedirs(paths.netlogon, 0755) os.makedirs(paths.netlogon, 0755)
secrets_ldb = Ldb(paths.secrets, session_info=session_info, credentials=credentials, lp=lp) secrets_ldb = Ldb(paths.secrets, session_info=session_info,
setup_ldb(secrets_ldb, setup_path("secrets_dc.ldif"), { credentials=credentials, lp=lp)
"MACHINEPASS_B64": b64encode(machinepass), secretsdb_become_dc(secrets_ldb, setup_path, domain=domain, realm=realm,
"DOMAIN": domain, netbiosname=netbiosname, domainsid=domainsid,
"REALM": realm, keytab_path=paths.keytab, samdb_url=paths.samdb,
"LDAPTIME": timestring(int(time.time())), dns_keytab_path=paths.dns_keytab, dnspass=dnspass,
"DNSDOMAIN": dnsdomain, machinepass=machinepass, dnsdomain=dnsdomain)
"DOMAINSID": str(domainsid),
"SECRETS_KEYTAB": paths.keytab,
"NETBIOSNAME": netbiosname,
"SAM_LDB": paths.samdb,
"DNS_KEYTAB": paths.dns_keytab,
"DNSPASS_B64": b64encode(dnspass),
})
if not blank: if samdb_fill == FILL_FULL:
setup_name_mappings(samdb, str(domainsid), setup_name_mappings(samdb, str(domainsid), domaindn, root=root,
domaindn, root=root, nobody=nobody, nobody=nobody, nogroup=nogroup, wheel=wheel,
nogroup=nogroup, wheel=wheel, users=users, users=users, backup=backup)
backup=backup)
message("Setting up sam.ldb rootDSE marking as synchronized")
setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
message("Setting up phpLDAPadmin configuration") message("Setting up phpLDAPadmin configuration")
create_phplpapdadmin_config(paths.phpldapadminconfig, setup_path, paths.s4_ldapi_path) create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
ldapi_url)
message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig) message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
@@ -816,15 +876,15 @@ def provision(lp, setup_dir, message, blank, paths, session_info,
return domaindn return domaindn
def create_phplpapdadmin_config(path, setup_path, s4_ldapi_path):
def create_phpldapadmin_config(path, setup_path, ldap_backend):
"""Create a PHP LDAP admin configuration file. """Create a PHP LDAP admin configuration file.
:param path: Path to write the configuration to. :param path: Path to write the configuration to.
:param setup_path: Function to generate setup paths. :param setup_path: Function to generate setup paths.
:param s4_ldapi_path: Path to Samba 4 LDAPI socket.
""" """
setup_file(setup_path("phpldapadmin-config.php"), setup_file(setup_path("phpldapadmin-config.php"), path,
path, {"S4_LDAPI_URI": "ldapi://%s" % s4_ldapi_path.replace("/", "%2F")}) {"S4_LDAPI_URI": ldap_backend})
def create_zone_file(path, setup_path, samdb, dnsdomain, domaindn, def create_zone_file(path, setup_path, samdb, dnsdomain, domaindn,
@@ -874,6 +934,7 @@ def load_schema(setup_path, samdb, schemadn, netbiosname, configdn):
"SCHEMADN": schemadn, "SCHEMADN": schemadn,
"NETBIOSNAME": netbiosname, "NETBIOSNAME": netbiosname,
"CONFIGDN": configdn, "CONFIGDN": configdn,
"DEFAULTSITE": DEFAULTSITE}) "DEFAULTSITE": DEFAULTSITE
})
samdb.attach_schema_from_ldif(head_data, schema_data) samdb.attach_schema_from_ldif(head_data, schema_data)

View File

@@ -1,10 +1,10 @@
#!/usr/bin/python #!/usr/bin/python
# Unix SMB/CIFS implementation. # Unix SMB/CIFS implementation.
# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2008
# #
# Based on the original in EJS: # Based on the original in EJS:
# Copyright (C) Andrew Tridgell 2005 # Copyright (C) Andrew Tridgell <tridge@samba.org> 2005
# #
# This program is free software; you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@@ -151,4 +151,4 @@ member: %s
:param invocation_id: GUID of the invocation id. :param invocation_id: GUID of the invocation id.
""" """
misc.samdb_set_ntds_invocation_id(self, invocation_id) misc.dsdb_set_ntds_invocation_id(self, invocation_id)

View File

@@ -1,7 +1,7 @@
#!/usr/bin/python #!/usr/bin/python
# Unix SMB/CIFS implementation. # Unix SMB/CIFS implementation.
# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2008
# #
# This program is free software; you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by

View File

@@ -7,7 +7,7 @@
"""Support code for upgrading from Samba 3 to Samba 4.""" """Support code for upgrading from Samba 3 to Samba 4."""
from provision import findnss, provision from provision import findnss, provision, FILL_DRS
import grp import grp
import ldb import ldb
import pwd import pwd
@@ -245,7 +245,8 @@ def upgrade_provision(samba3, setup_dir, message, credentials, session_info, lp,
else: else:
machinepass = None machinepass = None
domaindn = provision(lp=lp, setup_dir=setup_dir, message=message, blank=True, ldapbackend=None, domaindn = provision(lp=lp, setup_dir=setup_dir, message=message,
samdb_fill=FILL_DRS, ldapbackend=None,
paths=paths, session_info=session_info, credentials=credentials, realm=realm, paths=paths, session_info=session_info, credentials=credentials, realm=realm,
domain=domainname, domainsid=domainsid, domainguid=domainguid, domain=domainname, domainsid=domainsid, domainguid=domainguid,
machinepass=machinepass, serverrole=serverrole) machinepass=machinepass, serverrole=serverrole)

View File

@@ -33,8 +33,10 @@ import samba
from auth import system_session from auth import system_session
import samba.getopt as options import samba.getopt as options
import param import param
from samba.provision import (provision, from samba.provision import (provision,
provision_paths_from_lp) provision_paths_from_lp,
FILL_FULL, FILL_NT4SYNC,
FILL_DRS)
parser = optparse.OptionParser("provision [options]") parser = optparse.OptionParser("provision [options]")
sambaopts = options.SambaOptions(parser) sambaopts = options.SambaOptions(parser)
@@ -84,8 +86,9 @@ parser.add_option("--blank", action="store_true",
help="do not add users or groups, just the structure") help="do not add users or groups, just the structure")
parser.add_option("--ldap-backend", type="string", metavar="LDAPSERVER", parser.add_option("--ldap-backend", type="string", metavar="LDAPSERVER",
help="LDAP server to use for this provision") help="LDAP server to use for this provision")
parser.add_option("--ldap-module=", type="string", metavar="MODULE", parser.add_option("--ldap-backend-type", type="choice", metavar="LDAP-BACKEND-TYPE",
help="LDB mapping module to use for the LDAP backend") help="LDB mapping module to use for the LDAP backend",
choices=["fedora-ds", "openldap"])
parser.add_option("--aci", type="string", metavar="ACI", parser.add_option("--aci", type="string", metavar="ACI",
help="An arbitary LDIF fragment, particularly useful to loading a backend ACI value into a target LDAP server. You must provide at least a realm and domain") help="An arbitary LDIF fragment, particularly useful to loading a backend ACI value into a target LDAP server. You must provide at least a realm and domain")
parser.add_option("--server-role", type="choice", metavar="ROLE", parser.add_option("--server-role", type="choice", metavar="ROLE",
@@ -122,51 +125,47 @@ lp.set("realm", opts.realm)
lp.set("workgroup", opts.domain) lp.set("workgroup", opts.domain)
lp.set("server role", opts.server_role or "domain controller") lp.set("server role", opts.server_role or "domain controller")
if opts.aci is not None: if opts.aci is not None:
print "set ACI: %s" % opts.aci print "set ACI: %s" % opts.aci
paths = provision_paths_from_lp(lp, opts.realm.lower()) private_dir = None
if opts.targetdir is not None:
private_dir = os.path.join(opts.targetdir, "private")
paths = provision_paths_from_lp(lp, opts.realm.lower(), private_dir)
paths.smbconf = sambaopts.get_loadparm_path() paths.smbconf = sambaopts.get_loadparm_path()
if opts.ldap_backend:
if opts.ldap_backend == "ldapi":
subobj.ldap_backend = subobj.ldapi_uri
if not opts.ldap_module:
subobj.ldapmodule = "entryuuid"
subobj.domaindn_ldb = subobj.ldap_backend
subobj.domaindn_mod2 = ",%s,paged_searches" % subobj.ldapmodule
subobj.configdn_ldb = subobj.ldap_backend
subobj.configdn_mod2 = ",%s,paged_searches" % subobj.ldapmodule
subobj.schemadn_ldb = subobj.ldap_backend
subobj.schemadn_mod2 = ",%s,paged_searches" % subobj.ldapmodule
message("LDAP module: %s on backend: %s" % (subobj.ldapmodule, subobj.ldap_backend))
creds = credopts.get_credentials() creds = credopts.get_credentials()
setup_dir = opts.setupdir setup_dir = opts.setupdir
if setup_dir is None: if setup_dir is None:
setup_dir = "setup" setup_dir = "setup"
if opts.partitions_only:
provision_become_dc(setup_dir, message, False,
paths, lp, system_session(), creds)
else:
provision(lp, setup_dir, message, opts.blank, paths,
system_session(), creds, opts.ldap_backend, realm=opts.realm,
domainguid=opts.domain_guid, domainsid=opts.domain_sid,
policyguid=opts.policy_guid, hostname=opts.host_name,
hostip=opts.host_ip, hostguid=opts.host_guid,
invocationid=opts.invocationid, adminpass=opts.adminpass,
krbtgtpass=opts.krbtgtpass, machinepass=opts.machinepass,
dnspass=opts.dnspass, root=opts.root, nobody=opts.nobody,
nogroup=opts.nogroup, wheel=opts.wheel, users=opts.users,
aci=opts.aci, serverrole=opts.server_role)
message("To reproduce this provision, run with:")
def shell_escape(arg):
if " " in arg:
return '"%s"' % arg
return arg
message(" ".join([shell_escape(arg) for arg in sys.argv]))
message("All OK") samdb_fill = FILL_FULL
if opts.blank:
samdb_fill = FILL_NT4SYNC
elif opts.partitions_only:
samdb_fill = FILL_DRS
provision(lp, setup_dir, message, paths,
system_session(), creds, opts.ldap_backend,
samdb_fill=samdb_fill, realm=opts.realm,
domainguid=opts.domain_guid, domainsid=opts.domain_sid,
policyguid=opts.policy_guid, hostname=opts.host_name,
hostip=opts.host_ip, hostguid=opts.host_guid,
invocationid=opts.invocationid, adminpass=opts.adminpass,
krbtgtpass=opts.krbtgtpass, machinepass=opts.machinepass,
dnspass=opts.dnspass, root=opts.root, nobody=opts.nobody,
nogroup=opts.nogroup, wheel=opts.wheel, users=opts.users,
aci=opts.aci, serverrole=opts.server_role,
ldap_backend=opts.ldap_backend,
ldap_backend_type=opts.ldap_backend_type)
message("To reproduce this provision, run with:")
def shell_escape(arg):
if " " in arg:
return '"%s"' % arg
return arg
message(" ".join([shell_escape(arg) for arg in sys.argv]))
message("All OK")