import param
import registry
import samba
+from auth import system_session
from samba import Ldb, substitute_var, valid_netbios_name, check_all_substituted
from samba.samdb import SamDB
import security
self.hkpd = None
self.hkpt = None
self.samdb = None
+ self.idmapdb = None
self.secrets = None
self.keytab = None
self.dns_keytab = None
self.dns = None
self.winsdb = None
+ self.private_dir = None
def check_install(lp, session_info, credentials):
:param dnsdomain: DNS Domain name
"""
paths = ProvisionPaths()
- private_dir = lp.get("private dir")
+ paths.private_dir = lp.get("private dir")
paths.keytab = "secrets.keytab"
paths.dns_keytab = "dns.keytab"
- paths.shareconf = os.path.join(private_dir, "share.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.templates = os.path.join(private_dir, "templates.ldb")
- paths.dns = os.path.join(private_dir, dnsdomain + ".zone")
- paths.winsdb = os.path.join(private_dir, "wins.ldb")
- paths.s4_ldapi_path = os.path.join(private_dir, "ldapi")
- paths.smbconf = os.path.join(private_dir, "smb.conf")
- paths.phpldapadminconfig = os.path.join(private_dir,
+ paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
+ paths.samdb = os.path.join(paths.private_dir, lp.get("sam database") or "samdb.ldb")
+ paths.idmapdb = os.path.join(paths.private_dir, lp.get("idmap database") or "idmap.ldb")
+ paths.secrets = os.path.join(paths.private_dir, lp.get("secrets database") or "secrets.ldb")
+ paths.templates = os.path.join(paths.private_dir, "templates.ldb")
+ paths.dns = os.path.join(paths.private_dir, dnsdomain + ".zone")
+ paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
+ paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
+ paths.smbconf = os.path.join(paths.private_dir, "smb.conf")
+ paths.phpldapadminconfig = os.path.join(paths.private_dir,
"phpldapadmin-config.php")
paths.hklm = "hklm.ldb"
paths.hkcr = "hkcr.ldb"
def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
credentials, configdn, schemadn, domaindn,
hostname, netbiosname, dnsdomain, realm,
- rootdn, serverrole, ldap_backend=None,
+ rootdn, serverrole, sitename, 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.
-
:note: This will wipe the Sam Database!
:note: This function always removes the local SAM LDB file. The erase
"""
assert session_info is not None
- if os.path.exists(samdb_path):
+ samdb = SamDB(samdb_path, session_info=session_info,
+ credentials=credentials, lp=lp)
+
+ # Wipes the database
+ try:
+ samdb.erase()
+ except:
os.unlink(samdb_path)
- # Also wipes the database
samdb = SamDB(samdb_path, session_info=session_info,
credentials=credentials, lp=lp)
"server_sort",
"extended_dn",
"asq",
- "samldb",
"rdn_name",
"objectclass",
+ "samldb",
"kludge_acl",
"operational"]
tdb_modules_list = [
schemadn_ldb = "schema.ldb"
if ldap_backend is not None:
schema_ldb = ldap_backend
-
schemadn_ldb = ldap_backend
if ldap_backend_type == "fedora-ds":
- backend_modules = ["nsuniqueid","paged_searches"]
+ backend_modules = ["nsuniqueid", "paged_searches"]
+ # We can handle linked attributes here, as we don't have directory-side subtree operations
+ tdb_modules_list = ["linked_attributes"]
elif ldap_backend_type == "openldap":
- backend_modules = ["normalise","entryuuid","paged_searches"]
+ backend_modules = ["normalise", "entryuuid", "paged_searches"]
+ # OpenLDAP handles subtree renames, so we don't want to do any of these things
+ tdb_modules_list = None
elif serverrole == "domain controller":
backend_modules = ["repl_meta_data"]
else:
backend_modules = ["objectguid"]
+
+ if tdb_modules_list is None:
+ tdb_modules_list_as_string = ""
+ else:
+ tdb_modules_list_as_string = ","+",".join(tdb_modules_list)
samdb.transaction_start()
try:
"CONFIGDN_MOD": "naming_fsmo,instancetype",
"DOMAINDN_MOD": "pdc_fsmo,password_hash,instancetype",
"MODULES_LIST": ",".join(modules_list),
- "TDB_MODULES_LIST": ","+",".join(tdb_modules_list),
+ "TDB_MODULES_LIST": tdb_modules_list_as_string,
"MODULES_LIST2": ",".join(modules_list2),
"BACKEND_MOD": ",".join(backend_modules),
})
message("Setting up sam.ldb rootDSE")
setup_samdb_rootdse(samdb, setup_path, schemadn, domaindn, hostname,
- dnsdomain, realm, rootdn, configdn, netbiosname)
+ dnsdomain, realm, rootdn, configdn, netbiosname,
+ sitename)
if erase:
message("Erasing data from partitions")
assert os.path.exists(provision_reg)
reg.diff_apply(provision_reg)
+def setup_idmapdb(path, setup_path, session_info, credentials, lp):
+ """Setup the idmap database.
+
+ :param path: path to the idmap database
+ :param setup_path: Function that returns a path to a setup file
+ :param session_info: Session information
+ :param credentials: Credentials
+ :param lp: Loadparm context
+ """
+ if os.path.exists(path):
+ os.unlink(path)
+
+ idmap_ldb = Ldb(path, session_info=session_info, credentials=credentials,
+ lp=lp)
+
+ idmap_ldb.erase()
+ idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
+ return idmap_ldb
def setup_samdb_rootdse(samdb, setup_path, schemadn, domaindn, hostname,
- dnsdomain, realm, rootdn, configdn, netbiosname):
+ dnsdomain, realm, rootdn, configdn, netbiosname,
+ sitename):
"""Setup the SamDB rootdse.
:param samdb: Sam Database handle
"SCHEMADN": schemadn,
"NETBIOSNAME": netbiosname,
"DNSDOMAIN": dnsdomain,
- "DEFAULTSITE": DEFAULTSITE,
+ "DEFAULTSITE": sitename,
"REALM": realm,
"DNSNAME": "%s.%s" % (hostname, dnsdomain),
"DOMAINDN": domaindn,
def setup_self_join(samdb, configdn, schemadn, domaindn,
netbiosname, hostname, dnsdomain, machinepass, dnspass,
realm, domainname, domainsid, invocationid, setup_path,
- policyguid, hostguid=None):
+ policyguid, sitename, hostguid=None):
"""Join a host to its own domain."""
if hostguid is not None:
hostguid_add = "objectGUID: %s" % hostguid
"DOMAINDN": domaindn,
"INVOCATIONID": invocationid,
"NETBIOSNAME": netbiosname,
- "DEFAULTSITE": DEFAULTSITE,
+ "DEFAULTSITE": sitename,
"DNSNAME": "%s.%s" % (hostname, dnsdomain),
"MACHINEPASS_B64": b64encode(machinepass),
"DNSPASS_B64": b64encode(dnspass),
def setup_samdb(path, setup_path, session_info, credentials, lp,
schemadn, configdn, domaindn, dnsdomain, realm,
- netbiosname, message, hostname, rootdn, erase,
+ netbiosname, message, hostname, rootdn,
domainsid, aci, domainguid, policyguid,
domainname, fill, adminpass, krbtgtpass,
machinepass, hostguid, invocationid, dnspass,
- serverrole, ldap_backend=None, ldap_backend_type=None):
+ serverrole, sitename, ldap_backend=None,
+ ldap_backend_type=None):
"""Setup a complete SAM Database.
:note: This will wipe the main SAM database file!
"""
+ assert serverrole in ("domain controller", "member server")
+
+ erase = (fill != FILL_DRS)
+
# Also wipes the database
setup_samdb_partitions(path, setup_path, schemadn=schemadn, configdn=configdn,
domaindn=domaindn, message=message, lp=lp,
hostname=hostname, netbiosname=netbiosname,
dnsdomain=dnsdomain, realm=realm, rootdn=rootdn,
ldap_backend=ldap_backend, serverrole=serverrole,
- ldap_backend_type=ldap_backend_type, erase=erase)
+ ldap_backend_type=ldap_backend_type, erase=erase,
+ sitename=sitename)
samdb = SamDB(path, session_info=session_info,
credentials=credentials, lp=lp)
samdb = SamDB(path, session_info=session_info,
credentials=credentials, lp=lp)
samdb.set_domain_sid(domainsid)
- if lp.get("server role") == "domain controller":
+ if serverrole == "domain controller":
samdb.set_invocation_id(invocationid)
- load_schema(setup_path, samdb, schemadn, netbiosname, configdn)
+ load_schema(setup_path, samdb, schemadn, netbiosname, configdn, sitename)
samdb.transaction_start()
"DOMAINSID": str(domainsid),
"SCHEMADN": schemadn,
"NETBIOSNAME": netbiosname,
- "DEFAULTSITE": DEFAULTSITE,
+ "DEFAULTSITE": sitename,
"CONFIGDN": configdn,
"POLICYGUID": policyguid,
"DOMAINDN": domaindn,
setup_path("provision_schema_basedn_modify.ldif"), {
"SCHEMADN": schemadn,
"NETBIOSNAME": netbiosname,
- "DEFAULTSITE": DEFAULTSITE,
+ "DEFAULTSITE": sitename,
"CONFIGDN": configdn,
})
setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
"CONFIGDN": configdn,
"NETBIOSNAME": netbiosname,
- "DEFAULTSITE": DEFAULTSITE,
+ "DEFAULTSITE": sitename,
"DNSDOMAIN": dnsdomain,
"DOMAIN": domainname,
"SCHEMADN": schemadn,
setup_add_ldif(samdb, setup_path("provision.ldif"), {
"DOMAINDN": domaindn,
"NETBIOSNAME": netbiosname,
- "DEFAULTSITE": DEFAULTSITE,
+ "DEFAULTSITE": sitename,
"CONFIGDN": configdn,
})
"KRBTGTPASS_B64": b64encode(krbtgtpass),
})
- if lp.get("server role") == "domain controller":
+ if serverrole == "domain controller":
message("Setting up self join")
setup_self_join(samdb, configdn=configdn, schemadn=schemadn,
domaindn=domaindn, invocationid=invocationid,
machinepass=machinepass, domainname=domainname,
domainsid=domainsid, policyguid=policyguid,
hostname=hostname, hostguid=hostguid,
- setup_path=setup_path)
+ setup_path=setup_path, sitename=sitename)
#We want to setup the index last, as adds are faster unindexed
message("Setting up sam.ldb index")
samdb.transaction_commit()
return samdb
+
FILL_FULL = "FULL"
FILL_NT4SYNC = "NT4SYNC"
FILL_DRS = "DRS"
-def provision(lp, setup_dir, message, paths, session_info,
- credentials, samdb_fill=FILL_FULL, realm=None, rootdn=None,
+def provision(setup_dir, message, session_info,
+ credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL, realm=None,
+ rootdn=None, domaindn=None, schemadn=None, configdn=None,
domain=None, hostname=None, hostip=None, domainsid=None,
hostguid=None, adminpass=None, krbtgtpass=None, domainguid=None,
policyguid=None, invocationid=None, machinepass=None,
dnspass=None, root=None, nobody=None, nogroup=None, users=None,
- wheel=None, backup=None, aci=None, serverrole=None, erase=False,
- ldap_backend=None, ldap_backend_type=None):
+ wheel=None, backup=None, aci=None, serverrole=None,
+ ldap_backend=None, ldap_backend_type=None, sitename=DEFAULTSITE):
"""Provision samba4
:note: caution, this wipes all existing data!
backup = findnss(grp.getgrnam, ["backup", "wheel", "root", "staff"])[0]
if aci is None:
aci = "# no aci for local ldb"
+ if hostname is None:
+ hostname = gethostname().split(".")[0].lower()
+
+ if hostip is None:
+ hostip = gethostbyname(hostname)
+
+ netbiosname = hostname.upper()
+ if not valid_netbios_name(netbiosname):
+ raise InvalidNetbiosName(netbiosname)
+
+ if targetdir is not None:
+ if not os.path.exists(targetdir):
+ os.mkdir(targetdir)
+ if not os.path.exists(os.path.join(targetdir, "etc")):
+ os.mkdir(os.path.join(targetdir, "etc"))
+
+ if smbconf is None:
+ smbconf = os.path.join(targetdir, os.path.join("etc", "smb.conf"))
+
+ # only install a new smb.conf if there isn't one there already
+ if not os.path.exists(smbconf):
+ message("Setting up smb.conf")
+ assert serverrole is not None
+ if serverrole == "domain controller":
+ smbconfsuffix = "dc"
+ elif serverrole == "member server":
+ smbconfsuffix = "member"
+
+ assert domain is not None
+ assert realm is not None
+
+ default_lp = param.LoadParm()
+ #Load non-existant file
+ default_lp.load(smbconf)
+
+ if targetdir is not None:
+ privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
+ lockdir_line = "lock dir = " + os.path.abspath(targetdir)
+
+ default_lp.set("lock dir", os.path.abspath(targetdir))
+
+ sysvol = os.path.join(default_lp.get("lock dir"), "sysvol")
+ netlogon = os.path.join(os.path.join(sysvol, "scripts"))
+
+ setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
+ smbconf, {
+ "HOSTNAME": hostname,
+ "DOMAIN_CONF": domain,
+ "REALM_CONF": realm,
+ "SERVERROLE": serverrole,
+ "NETLOGONPATH": netlogon,
+ "SYSVOLPATH": sysvol,
+ "PRIVATEDIR_LINE": privatedir_line,
+ "LOCKDIR_LINE": lockdir_line
+ })
+
+ lp = param.LoadParm()
+ lp.load(smbconf)
+
if serverrole is None:
serverrole = lp.get("server role")
assert serverrole in ("domain controller", "member server")
if realm is None:
realm = lp.get("realm")
- if lp.get("realm").upper() != realm.upper():
- raise Exception("realm '%s' in smb.conf must match chosen realm '%s'" %
- (lp.get("realm"), realm))
-
- ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
-
- 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"), safe="")
-
assert realm is not None
realm = realm.upper()
- if hostname is None:
- hostname = gethostname().split(".")[0].lower()
+ dnsdomain = realm.lower()
- if hostip is None:
- hostip = gethostbyname(hostname)
+ paths = provision_paths_from_lp(lp, dnsdomain)
- netbiosname = hostname.upper()
- if not valid_netbios_name(netbiosname):
- raise InvalidNetbiosName(netbiosname)
+ if targetdir is not None:
+ if not os.path.exists(paths.private_dir):
+ os.mkdir(paths.private_dir)
+
+ ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
+
+ if ldap_backend == "ldapi":
+ # provision-backend will set this path suggested slapd command line / fedorads.inf
+ ldap_backend = "ldapi://" % urllib.quote(os.path.join(paths.private_dir, "ldap", "ldapi"), safe="")
- dnsdomain = realm.lower()
if serverrole == "domain controller":
- domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
+ if domaindn is None:
+ domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
if domain is None:
domain = lp.get("workgroup")
if not valid_netbios_name(domain):
raise InvalidNetbiosName(domain)
else:
- domaindn = "CN=" + netbiosname
+ if domaindn is None:
+ domaindn = "CN=" + netbiosname
domain = netbiosname
if rootdn is None:
rootdn = domaindn
- configdn = "CN=Configuration," + rootdn
- schemadn = "CN=Schema," + configdn
+ if configdn is None:
+ configdn = "CN=Configuration," + rootdn
+ if schemadn is None:
+ schemadn = "CN=Schema," + configdn
message("set DOMAIN SID: %s" % str(domainsid))
message("Provisioning for %s in realm %s" % (domain, realm))
message("Using administrator password: %s" % adminpass)
- assert paths.smbconf is not None
-
- # only install a new smb.conf if there isn't one there already
- if not os.path.exists(paths.smbconf):
- message("Setting up smb.conf")
- if serverrole == "domain controller":
- smbconfsuffix = "dc"
- elif serverrole == "member":
- smbconfsuffix = "member"
- setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
- paths.smbconf, {
- "HOSTNAME": hostname,
- "DOMAIN_CONF": domain,
- "REALM_CONF": realm,
- "SERVERROLE": serverrole,
- "NETLOGONPATH": paths.netlogon,
- "SYSVOLPATH": paths.sysvol,
- })
- lp.load(paths.smbconf)
+ if lp.get("realm").upper() != realm.upper():
+ raise Exception("realm '%s' in smb.conf must match chosen realm '%s'" %
+ (lp.get("realm"), realm))
# only install a new shares config db if there is none
if not os.path.exists(paths.shareconf):
setup_templatesdb(paths.templates, setup_path, session_info=session_info,
credentials=credentials, lp=lp)
+ message("Setting up idmap db")
+ setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
+ credentials=credentials, lp=lp)
+
samdb = setup_samdb(paths.samdb, setup_path, session_info=session_info,
credentials=credentials, lp=lp, schemadn=schemadn,
configdn=configdn, domaindn=domaindn,
dnsdomain=dnsdomain, netbiosname=netbiosname,
realm=realm, message=message, hostname=hostname,
- rootdn=rootdn, erase=erase, domainsid=domainsid,
+ rootdn=rootdn, domainsid=domainsid,
aci=aci, domainguid=domainguid, policyguid=policyguid,
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)
+ ldap_backend_type=ldap_backend_type, sitename=sitename)
if lp.get("server role") == "domain controller":
policy_path = os.path.join(paths.sysvol, dnsdomain, "Policies",
message("Setting up sam.ldb rootDSE marking as synchronized")
setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
+ # Only make a zone file on the first DC, it should be replicated with DNS replication
+ if serverrole == "domain controller":
+ samdb = SamDB(paths.samdb, session_info=session_info,
+ credentials=credentials, lp=lp)
+
+ domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
+ assert isinstance(domainguid, str)
+ hostguid = samdb.searchone(basedn=domaindn, attribute="objectGUID",
+ expression="(&(objectClass=computer)(cn=%s))" % hostname,
+ scope=SCOPE_SUBTREE)
+ assert isinstance(hostguid, str)
+
+ message("Setting up DNS zone: %s" % dnsdomain)
+ create_zone_file(paths.dns, setup_path, samdb,
+ hostname=hostname, hostip=hostip, dnsdomain=dnsdomain,
+ domaindn=domaindn, dnspass=dnspass, realm=realm,
+ domainguid=domainguid, hostguid=hostguid)
+ message("Please install the zone located in %s into your DNS server" % paths.dns)
+
message("Setting up phpLDAPadmin configuration")
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)
- if lp.get("server role") == "domain controller":
- samdb = SamDB(paths.samdb, session_info=session_info,
- credentials=credentials, lp=lp)
-
- domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
- assert isinstance(domainguid, str)
- hostguid = samdb.searchone(basedn=domaindn, attribute="objectGUID",
- expression="(&(objectClass=computer)(cn=%s))" % hostname,
- scope=SCOPE_SUBTREE)
- assert isinstance(hostguid, str)
-
- message("Setting up DNS zone: %s" % dnsdomain)
- create_zone_file(paths.dns, setup_path, samdb,
- hostname=hostname, hostip=hostip, dnsdomain=dnsdomain,
- domaindn=domaindn, dnspass=dnspass, realm=realm,
- domainguid=domainguid, hostguid=hostguid)
- message("Please install the zone located in %s into your DNS server" % paths.dns)
-
return domaindn
+def provision_become_dc(setup_dir=None,
+ smbconf=None, targetdir=None, realm=None,
+ rootdn=None, domaindn=None, schemadn=None, configdn=None,
+ domain=None, hostname=None, domainsid=None,
+ hostguid=None, adminpass=None, krbtgtpass=None, domainguid=None,
+ policyguid=None, invocationid=None, machinepass=None,
+ dnspass=None, root=None, nobody=None, nogroup=None, users=None,
+ wheel=None, backup=None, aci=None, serverrole=None,
+ ldap_backend=None, ldap_backend_type=None, sitename=DEFAULTSITE):
+
+ def message(text):
+ """print a message if quiet is not set."""
+ print text
+
+ provision(setup_dir, message, system_session(), None,
+ smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS, realm=realm,
+ rootdn=rootdn, domaindn=domaindn, schemadn=schemadn, configdn=configdn,
+ domain=domain, hostname=hostname, hostip="127.0.0.1", domainsid=domainsid, machinepass=machinepass, serverrole="domain controller", sitename=sitename);
+
def create_phpldapadmin_config(path, setup_path, ldapi_uri):
"""Create a PHP LDAP admin configuration file.
"HOSTGUID": hostguid,
})
-
-def load_schema(setup_path, samdb, schemadn, netbiosname, configdn):
+def load_schema(setup_path, samdb, schemadn, netbiosname, configdn, sitename):
"""Load schema for the SamDB.
:param samdb: Load a schema into a SamDB.
"SCHEMADN": schemadn,
"NETBIOSNAME": netbiosname,
"CONFIGDN": configdn,
- "DEFAULTSITE": DEFAULTSITE
+ "DEFAULTSITE":sitename
})
samdb.attach_schema_from_ldif(head_data, schema_data)