From 350e96354543943b5e7249d517596ddfaf29c47e Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Fri, 30 Oct 2009 13:16:10 +1100 Subject: s4:provision Rework provision to always have a ProvisionBackend Rather than treat the LDAP backend as a special case, treat all backends the same, with different callbacks. Andrew Bartlett --- source4/scripting/python/samba/provision.py | 353 +++++++++++++++------------- source4/setup/provision | 2 +- 2 files changed, 189 insertions(+), 166 deletions(-) (limited to 'source4') diff --git a/source4/scripting/python/samba/provision.py b/source4/scripting/python/samba/provision.py index 6c00127bc6..7558ad3d7c 100644 --- a/source4/scripting/python/samba/provision.py +++ b/source4/scripting/python/samba/provision.py @@ -184,7 +184,7 @@ class ProvisionResult(object): class Schema(object): def __init__(self, setup_path, domain_sid, schemadn=None, - serverdn=None, sambadn=None, ldap_backend_type=None): + serverdn=None, sambadn=None): """Load schema for the SamDB from the AD schema files and samba4_schema.ldif :param samdb: Load a schema into a SamDB. @@ -604,8 +604,8 @@ def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid, idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid) def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info, - credentials, names, schema, - serverrole, ldap_backend=None, + provision_backend, names, schema, + serverrole, erase=False): """Setup the partitions for the SAM database. @@ -628,7 +628,7 @@ def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info, try: samdb = Ldb(url=samdb_path, session_info=session_info, - credentials=credentials, lp=lp, options=["modules:"]) + lp=lp, options=["modules:"]) res = samdb.search(base="@PARTITION", scope=SCOPE_BASE, attrs=["partition"], expression="partition=*") if len(res) == 1: try: @@ -651,7 +651,7 @@ def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info, except LdbError: os.unlink(samdb_path) samdb = Ldb(url=samdb_path, session_info=session_info, - credentials=credentials, lp=lp, options=["modules:"]) + lp=lp, options=["modules:"]) # Wipes the database samdb.erase_except_schema_controlled() @@ -693,11 +693,12 @@ def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info, "schema_load", "new_partition", "partition"] + ldap_backend_line = "# No LDAP backend" - if ldap_backend is not None: - ldap_backend_line = "ldapBackend: %s" % ldap_backend.ldapi_uri + if provision_backend.type is not "ldb": + ldap_backend_line = "ldapBackend: %s" % provision_backend.ldapi_uri - if ldap_backend.ldap_backend_type == "fedora-ds": + if provision_backend.ldap_backend_type == "fedora-ds": backend_modules = ["nsuniqueid", "paged_searches"] # We can handle linked attributes here, as we don't have directory-side subtree operations tdb_modules_list = ["extended_dn_out_fds"] @@ -837,7 +838,7 @@ def secretsdb_setup_dns(secretsdb, setup_path, realm, dnsdomain, }) -def setup_secretsdb(path, setup_path, session_info, credentials, lp): +def setup_secretsdb(path, setup_path, session_info, backend_credentials, lp): """Setup the secrets database. :param path: Path to the secrets database. @@ -849,26 +850,26 @@ def setup_secretsdb(path, setup_path, session_info, credentials, lp): """ if os.path.exists(path): os.unlink(path) - secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials, + secrets_ldb = Ldb(path, session_info=session_info, lp=lp) secrets_ldb.erase() secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif")) - secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials, + secrets_ldb = Ldb(path, session_info=session_info, lp=lp) secrets_ldb.transaction_start() secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif")) - if credentials is not None and credentials.authentication_requested(): - if credentials.get_bind_dn() is not None: + if backend_credentials is not None and backend_credentials.authentication_requested(): + if backend_credentials.get_bind_dn() is not None: setup_add_ldif(secrets_ldb, setup_path("secrets_simple_ldap.ldif"), { - "LDAPMANAGERDN": credentials.get_bind_dn(), - "LDAPMANAGERPASS_B64": b64encode(credentials.get_password()) + "LDAPMANAGERDN": backend_credentials.get_bind_dn(), + "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password()) }) else: setup_add_ldif(secrets_ldb, setup_path("secrets_sasl_ldap.ldif"), { - "LDAPADMINUSER": credentials.get_username(), - "LDAPADMINREALM": credentials.get_realm(), - "LDAPADMINPASS_B64": b64encode(credentials.get_password()) + "LDAPADMINUSER": backend_credentials.get_username(), + "LDAPADMINREALM": backend_credentials.get_realm(), + "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password()) }) return secrets_ldb @@ -1003,13 +1004,13 @@ def setup_self_join(samdb, names, }) -def setup_samdb(path, setup_path, session_info, credentials, lp, +def setup_samdb(path, setup_path, session_info, provision_backend, lp, names, message, domainsid, domainguid, policyguid, policyguid_dc, fill, adminpass, krbtgtpass, machinepass, invocationid, dnspass, ntdsguid, serverrole, dom_for_fun_level=None, - schema=None, ldap_backend=None): + schema=None): """Setup a complete SAM Database. :note: This will wipe the main SAM database file! @@ -1032,17 +1033,17 @@ def setup_samdb(path, setup_path, session_info, credentials, lp, # Also wipes the database setup_samdb_partitions(path, setup_path, message=message, lp=lp, - credentials=credentials, session_info=session_info, - names=names, ldap_backend=ldap_backend, + provision_backend=provision_backend, session_info=session_info, + names=names, serverrole=serverrole, schema=schema) if (schema == None): schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn, - sambadn=names.sambadn, ldap_backend_type=ldap_backend.ldap_backend_type) + sambadn=names.sambadn) # Load the database, but importantly, use Ldb not SamDB as we don't want to load the global schema samdb = Ldb(session_info=session_info, - credentials=credentials, lp=lp) + credentials=provision_backend.credentials, lp=lp) message("Pre-loading the Samba 4 and AD schema") @@ -1222,7 +1223,7 @@ def provision(setup_dir, message, session_info, dnspass=None, root=None, nobody=None, users=None, wheel=None, backup=None, aci=None, serverrole=None, dom_for_fun_level=None, - ldap_backend_extra_port=None, ldap_backend_type=None, + ldap_backend_extra_port=None, backend_type=None, sitename=None, ol_mmr_urls=None, ol_olc=None, setup_ds_path=None, slapd_path=None, nosync=False, @@ -1260,8 +1261,11 @@ def provision(setup_dir, message, session_info, #Make a new, random password between Samba and it's LDAP server ldapadminpass=glue.generate_random_str(12) + if backend_type is None: + backend_type = "ldb" + sid_generator = "internal" - if ldap_backend_type == "fedora-ds": + if backend_type == "fedora-ds": sid_generator = "backend" root_uid = findnss_uid([root or "root"]) @@ -1319,44 +1323,34 @@ def provision(setup_dir, message, session_info, ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="") schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn, - sambadn=names.sambadn, ldap_backend_type=ldap_backend_type) + sambadn=names.sambadn) - secrets_credentials = credentials - provision_backend = None - if ldap_backend_type: - # We only support an LDAP backend over ldapi:// - - provision_backend = ProvisionBackend(paths=paths, setup_path=setup_path, - lp=lp, credentials=credentials, - names=names, - message=message, hostname=hostname, - root=root, schema=schema, - ldap_backend_type=ldap_backend_type, - ldapadminpass=ldapadminpass, - ldap_backend_extra_port=ldap_backend_extra_port, - ol_mmr_urls=ol_mmr_urls, - slapd_path=slapd_path, - setup_ds_path=setup_ds_path, - ldap_dryrun_mode=ldap_dryrun_mode, - domainsid=domainsid) - - # Now use the backend credentials to access the databases - credentials = provision_backend.credentials - secrets_credentials = provision_backend.adminCredentials - ldapi_url = provision_backend.ldapi_uri + provision_backend = ProvisionBackend(backend_type, + paths=paths, setup_path=setup_path, + lp=lp, credentials=credentials, + names=names, + message=message, hostname=hostname, + root=root, schema=schema, + ldapadminpass=ldapadminpass, + ldap_backend_extra_port=ldap_backend_extra_port, + ol_mmr_urls=ol_mmr_urls, + slapd_path=slapd_path, + setup_ds_path=setup_ds_path, + ldap_dryrun_mode=ldap_dryrun_mode, + domainsid=domainsid) # only install a new shares config db if there is none if not os.path.exists(paths.shareconf): message("Setting up share.ldb") share_ldb = Ldb(paths.shareconf, session_info=session_info, - credentials=credentials, lp=lp) + lp=lp) share_ldb.load_ldif_file_add(setup_path("share.ldif")) message("Setting up secrets.ldb") secrets_ldb = setup_secretsdb(paths.secrets, setup_path, session_info=session_info, - credentials=secrets_credentials, lp=lp) + backend_credentials=provision_backend.secrets_credentials, lp=lp) message("Setting up the registry") setup_registry(paths.hklm, setup_path, session_info, @@ -1370,9 +1364,9 @@ def provision(setup_dir, message, session_info, lp=lp) message("Setting up SAM db") - samdb = setup_samdb(paths.samdb, setup_path, session_info=session_info, - credentials=credentials, lp=lp, names=names, - message=message, + samdb = setup_samdb(paths.samdb, setup_path, session_info, + provision_backend, lp, names, + message, domainsid=domainsid, schema=schema, domainguid=domainguid, policyguid=policyguid, policyguid_dc=policyguid_dc, @@ -1381,8 +1375,7 @@ def provision(setup_dir, message, session_info, invocationid=invocationid, machinepass=machinepass, dnspass=dnspass, ntdsguid=ntdsguid, serverrole=serverrole, - dom_for_fun_level=dom_for_fun_level, - ldap_backend=provision_backend) + dom_for_fun_level=dom_for_fun_level) if serverrole == "domain controller": if paths.netlogon is None: @@ -1464,61 +1457,18 @@ def provision(setup_dir, message, session_info, realm=names.realm) message("A Kerberos configuration suitable for Samba 4 has been generated at %s" % paths.krb5conf) - #Now commit the secrets.ldb to disk - secrets_ldb.transaction_commit() - - if provision_backend is not None: - if ldap_backend_type == "fedora-ds": - ldapi_db = Ldb(provision_backend.ldapi_uri, lp=lp, credentials=credentials) - - # delete default SASL mappings - res = ldapi_db.search(expression="(!(cn=samba-admin mapping))", base="cn=mapping,cn=sasl,cn=config", scope=SCOPE_ONELEVEL, attrs=["dn"]) - - # configure in-directory access control on Fedora DS via the aci attribute (over a direct ldapi:// socket) - for i in range (0, len(res)): - dn = str(res[i]["dn"]) - ldapi_db.delete(dn) - - aci = """(targetattr = "*") (version 3.0;acl "full access to all by samba-admin";allow (all)(userdn = "ldap:///CN=samba-admin,%s");)""" % names.sambadn - - m = ldb.Message() - m["aci"] = ldb.MessageElement([aci], ldb.FLAG_MOD_REPLACE, "aci") - - m.dn = ldb.Dn(1, names.domaindn) - ldapi_db.modify(m) - - m.dn = ldb.Dn(1, names.configdn) - ldapi_db.modify(m) - - m.dn = ldb.Dn(1, names.schemadn) - ldapi_db.modify(m) - - # if an LDAP backend is in use, terminate slapd after final provision and check its proper termination - if provision_backend.slapd.poll() is None: - #Kill the slapd - if hasattr(provision_backend.slapd, "terminate"): - provision_backend.slapd.terminate() - else: - # Older python versions don't have .terminate() - import signal - os.kill(provision_backend.slapd.pid, signal.SIGTERM) - - #and now wait for it to die - provision_backend.slapd.communicate() - - # now display slapd_command_file.txt to show how slapd must be started next time - message("Use later the following commandline to start slapd, then Samba:") - slapd_command = "\'" + "\' \'".join(provision_backend.slapd_command) + "\'" - message(slapd_command) - message("This slapd-Commandline is also stored under: " + paths.ldapdir + "/ldap_backend_startup.sh") - - setup_file(setup_path("ldap_backend_startup.sh"), paths.ldapdir + "/ldap_backend_startup.sh", { - "SLAPD_COMMAND" : slapd_command}) + if provision_backend.post_setup is not None: + provision_backend.post_setup() + if provision_backend.shutdown is not None: + provision_backend.shutdown() create_phpldapadmin_config(paths.phpldapadminconfig, setup_path, ldapi_url) + #Now commit the secrets.ldb to disk + secrets_ldb.transaction_commit() + message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig) message("Once the above files are installed, your Samba4 server will be ready to use") @@ -1529,14 +1479,21 @@ def provision(setup_dir, message, session_info, message("DOMAIN SID: %s" % str(domainsid)) if samdb_fill == FILL_FULL: message("Admin password: %s" % adminpass) - if provision_backend: + if provision_backend.type is not "ldb": if provision_backend.credentials.get_bind_dn() is not None: message("LDAP Backend Admin DN: %s" % provision_backend.credentials.get_bind_dn()) else: message("LDAP Admin User: %s" % provision_backend.credentials.get_username()) message("LDAP Admin Password: %s" % provision_backend.credentials.get_password()) - + + if provision_backend.slapd_command_escaped is not None: + # now display slapd_command_file.txt to show how slapd must be started next time + message("Use later the following commandline to start slapd, then Samba:") + message(provision_backend.slapd_command_escaped) + message("This slapd-Commandline is also stored under: " + paths.ldapdir + "/ldap_backend_startup.sh") + + result = ProvisionResult() result.domaindn = domaindn result.paths = paths @@ -1586,13 +1543,28 @@ def setup_db_config(setup_path, dbdir): setup_file(setup_path("DB_CONFIG"), os.path.join(dbdir, "DB_CONFIG"), {"LDAPDBDIR": dbdir}) - + +def ldap_backend_shutdown(self): + # if an LDAP backend is in use, terminate slapd after final provision and check its proper termination + if self.slapd.poll() is None: + #Kill the slapd + if hasattr(self.slapd, "terminate"): + self.slapd.terminate() + else: + # Older python versions don't have .terminate() + import signal + os.kill(self.slapd.pid, signal.SIGTERM) + + #and now wait for it to die + self.slapd.communicate() + + class ProvisionBackend(object): - def __init__(self, paths=None, setup_path=None, lp=None, credentials=None, + def __init__(self, backend_type, paths=None, setup_path=None, lp=None, credentials=None, names=None, message=None, hostname=None, root=None, schema=None, ldapadminpass=None, - ldap_backend_type=None, ldap_backend_extra_port=None, + ldap_backend_extra_port=None, ol_mmr_urls=None, setup_ds_path=None, slapd_path=None, nosync=False, ldap_dryrun_mode=False, @@ -1601,21 +1573,36 @@ class ProvisionBackend(object): This works for OpenLDAP and Fedora DS """ + self.paths = paths + self.slapd_command = None + self.slapd_command_escaped = None + + self.type = backend_type + + # Set a default - the code for "existing" below replaces this + self.ldap_backend_type = backend_type + + self.post_setup = None + self.shutdown = None + + if self.type is "ldb": + self.credentials = None + self.secrets_credentials = None + return self.ldapi_uri = "ldapi://" + urllib.quote(os.path.join(paths.ldapdir, "ldapi"), safe="") - if not os.path.isdir(paths.ldapdir): - os.makedirs(paths.ldapdir, 0700) - - if ldap_backend_type == "existing": + if self.type == "existing": #Check to see that this 'existing' LDAP backend in fact exists ldapi_db = Ldb(self.ldapi_uri, credentials=credentials) search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE, expression="(objectClass=OpenLDAProotDSE)") # If we have got here, then we must have a valid connection to the LDAP server, with valid credentials supplied - # This caused them to be set into the long-term database later in the script. self.credentials = credentials + # This caused them to be set into the long-term database later in the script. + self.secrets_credentials = credentials + self.ldap_backend_type = "openldap" #For now, assume existing backends at least emulate OpenLDAP return @@ -1645,13 +1632,16 @@ class ProvisionBackend(object): message (slapd_path) raise ProvisioningError("Warning: Given Path to slapd does not exist!") + + if not os.path.isdir(paths.ldapdir): + os.makedirs(paths.ldapdir, 0700) + schemadb_path = os.path.join(paths.ldapdir, "schema-tmp.ldb") try: os.unlink(schemadb_path) except OSError: pass - # Put the LDIF of the schema into a database so we can search on # it to generate schema-dependent configurations in Fedora DS and # OpenLDAP @@ -1670,15 +1660,15 @@ class ProvisionBackend(object): #Kerberos to an ldapi:// backend makes no sense self.credentials.set_kerberos_state(DONT_USE_KERBEROS) - self.adminCredentials = Credentials() - self.adminCredentials.guess(lp) + self.secrets_credentials = Credentials() + self.secrets_credentials.guess(lp) #Kerberos to an ldapi:// backend makes no sense - self.adminCredentials.set_kerberos_state(DONT_USE_KERBEROS) + self.secrets_credentials.set_kerberos_state(DONT_USE_KERBEROS) - self.ldap_backend_type = ldap_backend_type + self.shutdown = ldap_backend_shutdown - if ldap_backend_type == "fedora-ds": - provision_fds_backend(self, paths=paths, setup_path=setup_path, + if self.type == "fedora-ds": + provision_fds_backend(self, setup_path=setup_path, names=names, message=message, hostname=hostname, ldapadminpass=ldapadminpass, root=root, @@ -1690,8 +1680,8 @@ class ProvisionBackend(object): ldap_dryrun_mode=ldap_dryrun_mode, domainsid=domainsid) - elif ldap_backend_type == "openldap": - provision_openldap_backend(self, paths=paths, setup_path=setup_path, + elif self.type == "openldap": + provision_openldap_backend(self, setup_path=setup_path, names=names, message=message, hostname=hostname, ldapadminpass=ldapadminpass, root=root, @@ -1705,8 +1695,12 @@ class ProvisionBackend(object): raise ProvisioningError("Unknown LDAP backend type selected") self.credentials.set_password(ldapadminpass) - self.adminCredentials.set_username("samba-admin") - self.adminCredentials.set_password(ldapadminpass) + self.secrets_credentials.set_username("samba-admin") + self.secrets_credentials.set_password(ldapadminpass) + + self.slapd_command_escaped = "\'" + "\' \'".join(self.slapd_command) + "\'" + setup_file(setup_path("ldap_backend_startup.sh"), paths.ldapdir + "/ldap_backend_startup.sh", { + "SLAPD_COMMAND" : slapd_command}) # Now start the slapd, so we can provision onto it. We keep the # subprocess context around, to kill this off at the successful @@ -1728,7 +1722,7 @@ class ProvisionBackend(object): raise ProvisioningError("slapd died before we could make a connection to it") -def provision_openldap_backend(result, paths=None, setup_path=None, names=None, +def provision_openldap_backend(result, setup_path=None, names=None, message=None, hostname=None, ldapadminpass=None, root=None, schema=None, @@ -1840,15 +1834,15 @@ def provision_openldap_backend(result, paths=None, setup_path=None, names=None, { "RID" : str(rid), "LDAPSERVER" : url}) - setup_file(setup_path("olc_seed.ldif"), paths.olcseedldif, + setup_file(setup_path("olc_seed.ldif"), result.paths.olcseedldif, {"OLC_SERVER_ID_CONF": olc_serverids_config, "OLC_PW": ldapadminpass, "OLC_SYNCREPL_CONF": olc_syncrepl_seed_config}) # end olc - setup_file(setup_path("slapd.conf"), paths.slapdconf, + setup_file(setup_path("slapd.conf"), result.paths.slapdconf, {"DNSDOMAIN": names.dnsdomain, - "LDAPDIR": paths.ldapdir, + "LDAPDIR": result.paths.ldapdir, "DOMAINDN": names.domaindn, "CONFIGDN": names.configdn, "SCHEMADN": names.schemadn, @@ -1865,26 +1859,26 @@ def provision_openldap_backend(result, paths=None, setup_path=None, names=None, "INDEX_CONFIG": index_config, "NOSYNC": nosync_config}) - setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "user")) - setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "config")) - setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "schema")) + setup_db_config(setup_path, os.path.join(result.paths.ldapdir, "db", "user")) + setup_db_config(setup_path, os.path.join(result.paths.ldapdir, "db", "config")) + setup_db_config(setup_path, os.path.join(result.paths.ldapdir, "db", "schema")) - if not os.path.exists(os.path.join(paths.ldapdir, "db", "samba", "cn=samba")): - os.makedirs(os.path.join(paths.ldapdir, "db", "samba", "cn=samba"), 0700) + if not os.path.exists(os.path.join(result.paths.ldapdir, "db", "samba", "cn=samba")): + os.makedirs(os.path.join(result.paths.ldapdir, "db", "samba", "cn=samba"), 0700) setup_file(setup_path("cn=samba.ldif"), - os.path.join(paths.ldapdir, "db", "samba", "cn=samba.ldif"), + os.path.join(result.paths.ldapdir, "db", "samba", "cn=samba.ldif"), { "UUID": str(uuid.uuid4()), "LDAPTIME": timestring(int(time.time()))} ) setup_file(setup_path("cn=samba-admin.ldif"), - os.path.join(paths.ldapdir, "db", "samba", "cn=samba", "cn=samba-admin.ldif"), + os.path.join(result.paths.ldapdir, "db", "samba", "cn=samba", "cn=samba-admin.ldif"), {"LDAPADMINPASS_B64": b64encode(ldapadminpass), "UUID": str(uuid.uuid4()), "LDAPTIME": timestring(int(time.time()))} ) if ol_mmr_urls is not None: setup_file(setup_path("cn=replicator.ldif"), - os.path.join(paths.ldapdir, "db", "samba", "cn=samba", "cn=replicator.ldif"), + os.path.join(result.paths.ldapdir, "db", "samba", "cn=samba", "cn=replicator.ldif"), {"MMR_PASSWORD_B64": b64encode(mmr_pass), "UUID": str(uuid.uuid4()), "LDAPTIME": timestring(int(time.time()))} ) @@ -1895,7 +1889,7 @@ def provision_openldap_backend(result, paths=None, setup_path=None, names=None, backend_schema_data = schema.ldb.convert_schema_to_openldap("openldap", open(setup_path(mapping), 'r').read()) assert backend_schema_data is not None - open(os.path.join(paths.ldapdir, backend_schema), 'w').write(backend_schema_data) + open(os.path.join(result.paths.ldapdir, backend_schema), 'w').write(backend_schema_data) # now we generate the needed strings to start slapd automatically, # first ldapi_uri... @@ -1913,7 +1907,7 @@ def provision_openldap_backend(result, paths=None, setup_path=None, names=None, # Prepare the 'result' information - the commands to return in particular result.slapd_provision_command = [slapd_path] - result.slapd_provision_command.append("-F" + paths.olcdir) + result.slapd_provision_command.append("-F" + result.paths.olcdir) result.slapd_provision_command.append("-h") @@ -1939,10 +1933,10 @@ def provision_openldap_backend(result, paths=None, setup_path=None, names=None, sys.exit(0) # Finally, convert the configuration into cn=config style! - if not os.path.isdir(paths.olcdir): - os.makedirs(paths.olcdir, 0770) + if not os.path.isdir(result.paths.olcdir): + os.makedirs(result.paths.olcdir, 0770) - retcode = subprocess.call([slapd_path, "-Ttest", "-f", paths.slapdconf, "-F", paths.olcdir], close_fds=True, shell=False) + retcode = subprocess.call([slapd_path, "-Ttest", "-f", result.paths.slapdconf, "-F", result.paths.olcdir], close_fds=True, shell=False) # We can't do this, as OpenLDAP is strange. It gives an error # output to the above, but does the conversion sucessfully... @@ -1950,14 +1944,14 @@ def provision_openldap_backend(result, paths=None, setup_path=None, names=None, # if retcode != 0: # raise ProvisioningError("conversion from slapd.conf to cn=config failed") - if not os.path.exists(os.path.join(paths.olcdir, "cn=config.ldif")): + if not os.path.exists(os.path.join(result.paths.olcdir, "cn=config.ldif")): raise ProvisioningError("conversion from slapd.conf to cn=config failed") # Don't confuse the admin by leaving the slapd.conf around - os.remove(paths.slapdconf) - + os.remove(result.paths.slapdconf) + -def provision_fds_backend(result, paths=None, setup_path=None, names=None, +def provision_fds_backend(result, setup_path=None, names=None, message=None, hostname=None, ldapadminpass=None, root=None, schema=None, @@ -1973,33 +1967,33 @@ def provision_fds_backend(result, paths=None, setup_path=None, names=None, else: serverport = "" - setup_file(setup_path("fedorads.inf"), paths.fedoradsinf, + setup_file(setup_path("fedorads.inf"), result.paths.fedoradsinf, {"ROOT": root, "HOSTNAME": hostname, "DNSDOMAIN": names.dnsdomain, - "LDAPDIR": paths.ldapdir, + "LDAPDIR": result.paths.ldapdir, "DOMAINDN": names.domaindn, "LDAPMANAGERDN": names.ldapmanagerdn, "LDAPMANAGERPASS": ldapadminpass, "SERVERPORT": serverport}) - setup_file(setup_path("fedorads-partitions.ldif"), paths.fedoradspartitions, + setup_file(setup_path("fedorads-partitions.ldif"), result.paths.fedoradspartitions, {"CONFIGDN": names.configdn, "SCHEMADN": names.schemadn, "SAMBADN": names.sambadn, }) - setup_file(setup_path("fedorads-sasl.ldif"), paths.fedoradssasl, + setup_file(setup_path("fedorads-sasl.ldif"), result.paths.fedoradssasl, {"SAMBADN": names.sambadn, }) - setup_file(setup_path("fedorads-dna.ldif"), paths.fedoradsdna, + setup_file(setup_path("fedorads-dna.ldif"), result.paths.fedoradsdna, {"DOMAINDN": names.domaindn, "SAMBADN": names.sambadn, "DOMAINSID": str(domainsid), }) - setup_file(setup_path("fedorads-pam.ldif"), paths.fedoradspam) + setup_file(setup_path("fedorads-pam.ldif"), result.paths.fedoradspam) lnkattr = get_linked_attributes(names.schemadn,schema.ldb) @@ -2020,8 +2014,8 @@ def provision_fds_backend(result, paths=None, setup_path=None, names=None, { "ATTR" : attr }) argnum += 1 - open(paths.fedoradsrefint, 'w').write(refint_config) - open(paths.fedoradslinkedattributes, 'w').write(memberof_config) + open(result.paths.fedoradsrefint, 'w').write(refint_config) + open(result.paths.fedoradslinkedattributes, 'w').write(memberof_config) attrs = ["lDAPDisplayName"] res = schema.ldb.search(expression="(&(objectclass=attributeSchema)(searchFlags:1.2.840.113556.1.4.803:=1))", base=names.schemadn, scope=SCOPE_ONELEVEL, attrs=attrs) @@ -2035,9 +2029,9 @@ def provision_fds_backend(result, paths=None, setup_path=None, names=None, index_config += read_and_sub_file(setup_path("fedorads-index.ldif"), { "ATTR" : attr }) - open(paths.fedoradsindex, 'w').write(index_config) + open(result.paths.fedoradsindex, 'w').write(index_config) - setup_file(setup_path("fedorads-samba.ldif"), paths.fedoradssamba, + setup_file(setup_path("fedorads-samba.ldif"), result.paths.fedoradssamba, {"SAMBADN": names.sambadn, "LDAPADMINPASS": ldapadminpass }) @@ -2048,20 +2042,20 @@ def provision_fds_backend(result, paths=None, setup_path=None, names=None, # Build a schema file in Fedora DS format backend_schema_data = schema.ldb.convert_schema_to_openldap("fedora-ds", open(setup_path(mapping), 'r').read()) assert backend_schema_data is not None - open(os.path.join(paths.ldapdir, backend_schema), 'w').write(backend_schema_data) + open(os.path.join(result.paths.ldapdir, backend_schema), 'w').write(backend_schema_data) result.credentials.set_bind_dn(names.ldapmanagerdn) # Destory the target directory, or else setup-ds.pl will complain - fedora_ds_dir = os.path.join(paths.ldapdir, "slapd-samba4") + fedora_ds_dir = os.path.join(result.paths.ldapdir, "slapd-samba4") shutil.rmtree(fedora_ds_dir, True) - result.slapd_provision_command = [slapd_path, "-D", fedora_ds_dir, "-i", paths.slapdpid]; + result.slapd_provision_command = [slapd_path, "-D", fedora_ds_dir, "-i", result.paths.slapdpid]; #In the 'provision' command line, stay in the foreground so we can easily kill it result.slapd_provision_command.append("-d0") #the command for the final run is the normal script - result.slapd_command = [os.path.join(paths.ldapdir, "slapd-samba4", "start-slapd")] + result.slapd_command = [os.path.join(result.paths.ldapdir, "slapd-samba4", "start-slapd")] # If we were just looking for crashes up to this point, it's a # good time to exit before we realise we don't have Fedora DS on @@ -2076,17 +2070,46 @@ def provision_fds_backend(result, paths=None, setup_path=None, names=None, raise ProvisioningError("Warning: Given Path to slapd does not exist!") # Run the Fedora DS setup utility - retcode = subprocess.call([setup_ds_path, "--silent", "--file", paths.fedoradsinf], close_fds=True, shell=False) + retcode = subprocess.call([setup_ds_path, "--silent", "--file", result.paths.fedoradsinf], close_fds=True, shell=False) if retcode != 0: raise ProvisioningError("setup-ds failed") # Load samba-admin retcode = subprocess.call([ - os.path.join(paths.ldapdir, "slapd-samba4", "ldif2db"), "-s", names.sambadn, "-i", paths.fedoradssamba], + os.path.join(result.paths.ldapdir, "slapd-samba4", "ldif2db"), "-s", names.sambadn, "-i", result.paths.fedoradssamba], close_fds=True, shell=False) if retcode != 0: raise("ldib2db failed") + # Leave a hook to do the 'post initilisation' setup + def fds_post_setup(self): + ldapi_db = Ldb(self.ldapi_uri, credentials=self.credentials) + + # delete default SASL mappings + res = ldapi_db.search(expression="(!(cn=samba-admin mapping))", base="cn=mapping,cn=sasl,cn=config", scope=SCOPE_ONELEVEL, attrs=["dn"]) + + # configure in-directory access control on Fedora DS via the aci attribute (over a direct ldapi:// socket) + for i in range (0, len(res)): + dn = str(res[i]["dn"]) + ldapi_db.delete(dn) + + aci = """(targetattr = "*") (version 3.0;acl "full access to all by samba-admin";allow (all)(userdn = "ldap:///CN=samba-admin,%s");)""" % names.sambadn + + m = ldb.Message() + m["aci"] = ldb.MessageElement([aci], ldb.FLAG_MOD_REPLACE, "aci") + + m.dn = ldb.Dn(1, names.domaindn) + ldapi_db.modify(m) + + m.dn = ldb.Dn(1, names.configdn) + ldapi_db.modify(m) + + m.dn = ldb.Dn(1, names.schemadn) + ldapi_db.modify(m) + + result.post_setup = fds_post_setup + + def create_phpldapadmin_config(path, setup_path, ldapi_uri): """Create a PHP LDAP admin configuration file. diff --git a/source4/setup/provision b/source4/setup/provision index ddcf1cffb2..ed350dd46a 100755 --- a/source4/setup/provision +++ b/source4/setup/provision @@ -207,7 +207,7 @@ provision(setup_dir, message, wheel=opts.wheel, users=opts.users, serverrole=server_role, dom_for_fun_level=dom_for_fun_level, ldap_backend_extra_port=opts.ldap_backend_extra_port, - ldap_backend_type=opts.ldap_backend_type, + backend_type=opts.ldap_backend_type, ldapadminpass=opts.ldapadminpass, ol_mmr_urls=opts.ol_mmr_urls, slapd_path=opts.slapd_path, setup_ds_path=opts.setup_ds_path, nosync=opts.nosync,ldap_dryrun_mode=opts.ldap_dryrun_mode) -- cgit