s4:provision.py - remove hardcoded SIDs and RIDs
[ira/wip.git] / source4 / scripting / python / samba / provision.py
index 188c5909f6e6a17ef4c37684569557bfcf847360..af956579a8f1b6c36ea902efc5aaedbf471f927f 100644 (file)
@@ -52,7 +52,8 @@ import urllib
 from ldb import SCOPE_SUBTREE, SCOPE_ONELEVEL, SCOPE_BASE, LdbError
 from ms_display_specifiers import read_ms_ldif
 from schema import Schema
-from provisionbackend import ProvisionBackend
+from provisionbackend import LDBBackend, ExistingBackend, FDSBackend, OpenLDAPBackend
+from provisionexceptions import ProvisioningError, InvalidNetbiosName
 from signal import SIGTERM
 from dcerpc.misc import SEC_CHAN_BDC, SEC_CHAN_WKSTA
 
@@ -90,7 +91,7 @@ def get_config_descriptor(domain_sid):
            "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
            "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
            "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
-           "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;S-1-5-21-3191434175-1265308384-3577286990-498)" \
+           "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
            "S:(AU;SA;WPWOWD;;;WD)(AU;SA;CR;;;BA)(AU;SA;CR;;;DU)" \
            "(OU;SA;CR;45ec5156-db7e-47bb-b53f-dbeb2d03c40f;;WD)"
     sec = security.descriptor.from_sddl(sddl, domain_sid)
@@ -107,7 +108,7 @@ def get_domain_descriptor(domain_sid):
     "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
     "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
     "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
-    "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;S-1-5-21-832762594-175224951-1765713900-498)" \
+    "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
     "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;DD)" \
     "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a86-0de6-11d0-a285-00aa003049e2;ED)" \
     "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a9c-0de6-11d0-a285-00aa003049e2;ED)" \
@@ -118,7 +119,7 @@ def get_domain_descriptor(domain_sid):
     "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
     "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
     "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
-    "(OA;;CR;e2a36dc9-ae17-47c3-b58b-be34c55ba633;;S-1-5-32-557)" \
+    "(OA;;CR;e2a36dc9-ae17-47c3-b58b-be34c55ba633;;IF)" \
     "(OA;;RP;c7407360-20bf-11d0-a768-00aa006e0529;;RU)" \
     "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;RU)" \
     "(OA;CIIO;RPLCLORC;;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
@@ -153,15 +154,6 @@ DEFAULTSITE = "Default-First-Site-Name"
 
 # Exception classes
 
-class ProvisioningError(Exception):
-    """A generic provision error."""
-
-class InvalidNetbiosName(Exception):
-    """A specified name was not a valid NetBIOS name."""
-    def __init__(self, name):
-        super(InvalidNetbiosName, self).__init__("The name '%r' is not a valid NetBIOS name" % name)
-
-
 class ProvisionPaths(object):
     def __init__(self):
         self.shareconf = None
@@ -183,15 +175,6 @@ class ProvisionPaths(object):
         self.slapdconf = None
         self.modulesconf = None
         self.memberofconf = None
-        self.fedoradsinf = None
-        self.fedoradspartitions = None
-        self.fedoradssasl = None
-        self.fedoradsdna = None
-        self.fedoradspam = None
-        self.fedoradsrefint = None
-        self.fedoradslinkedattributes = None
-        self.fedoradsindex = None
-        self.fedoradssamba = None
         self.olmmron = None
         self.olmmrserveridsconf = None
         self.olmmrsyncreplconf = None
@@ -206,7 +189,6 @@ class ProvisionNames(object):
         self.domaindn = None
         self.configdn = None
         self.schemadn = None
-        self.sambadn = None
         self.ldapmanagerdn = None
         self.dnsdomain = None
         self.realm = None
@@ -335,24 +317,6 @@ def provision_paths_from_lp(lp, dnsdomain):
                                      "modules.conf")
     paths.memberofconf = os.path.join(paths.ldapdir, 
                                       "memberof.conf")
-    paths.fedoradsinf = os.path.join(paths.ldapdir, 
-                                     "fedorads.inf")
-    paths.fedoradspartitions = os.path.join(paths.ldapdir, 
-                                            "fedorads-partitions.ldif")
-    paths.fedoradssasl = os.path.join(paths.ldapdir, 
-                                      "fedorads-sasl.ldif")
-    paths.fedoradsdna = os.path.join(paths.ldapdir, 
-                                     "fedorads-dna.ldif")
-    paths.fedoradspam = os.path.join(paths.ldapdir,
-                                     "fedorads-pam.ldif")
-    paths.fedoradsrefint = os.path.join(paths.ldapdir,
-                                        "fedorads-refint.ldif")
-    paths.fedoradslinkedattributes = os.path.join(paths.ldapdir,
-                                                  "fedorads-linked-attributes.ldif")
-    paths.fedoradsindex = os.path.join(paths.ldapdir,
-                                       "fedorads-index.ldif")
-    paths.fedoradssamba = os.path.join(paths.ldapdir, 
-                                       "fedorads-samba.ldif")
     paths.olmmrserveridsconf = os.path.join(paths.ldapdir, 
                                             "mmr_serverids.conf")
     paths.olmmrsyncreplconf = os.path.join(paths.ldapdir, 
@@ -379,7 +343,7 @@ def provision_paths_from_lp(lp, dnsdomain):
 
 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
                 serverrole=None, rootdn=None, domaindn=None, configdn=None,
-                schemadn=None, serverdn=None, sitename=None, sambadn=None):
+                schemadn=None, serverdn=None, sitename=None):
     """Guess configuration settings to use."""
 
     if hostname is None:
@@ -441,8 +405,6 @@ def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
         configdn = "CN=Configuration," + rootdn
     if schemadn is None:
         schemadn = "CN=Schema," + configdn
-    if sambadn is None:
-        sambadn = "CN=Samba"
 
     if sitename is None:
         sitename=DEFAULTSITE
@@ -452,7 +414,6 @@ def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
     names.domaindn = domaindn
     names.configdn = configdn
     names.schemadn = schemadn
-    names.sambadn = sambadn
     names.ldapmanagerdn = "CN=Manager," + rootdn
     names.dnsdomain = dnsdomain
     names.domain = domain
@@ -580,89 +541,25 @@ def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
     samdb = Ldb(url=samdb_path, session_info=session_info, 
                 lp=lp, options=["modules:"])
 
-    #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 = ["resolve_oids",
-                    "rootdse",
-                    "lazy_commit",
-                    "acl",
-                    "paged_results",
-                    "ranged_results",
-                    "anr",
-                    "server_sort",
-                    "asq",
-                    "extended_dn_store",
-                    "extended_dn_in",
-                    "rdn_name",
-                    "objectclass",
-                    "descriptor",
-                    "samldb",
-                    "password_hash",
-                    "operational",
-                    "kludge_acl", 
-                    "instancetype"]
-    tdb_modules_list = [
-                    "subtree_rename",
-                    "subtree_delete",
-                    "linked_attributes",
-                    "extended_dn_out_ldb"]
-    modules_list2 = ["show_deleted",
-                     "schema_load",
-                     "new_partition",
-                     "partition"]
-
     ldap_backend_line = "# No LDAP backend"
     if provision_backend.type is not "ldb":
         ldap_backend_line = "ldapBackend: %s" % provision_backend.ldapi_uri
-        
-        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"]
-        elif ldap_backend.ldap_backend_type == "openldap":
-            backend_modules = ["entryuuid", "paged_searches"]
-            # OpenLDAP handles subtree renames, so we don't want to do any of these things
-            tdb_modules_list = ["extended_dn_out_openldap"]
-
-    elif serverrole == "domain controller":
-        tdb_modules_list.insert(0, "repl_meta_data")
-        backend_modules = []
-    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:
         message("Setting up sam.ldb partitions and settings")
         setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
                 "SCHEMADN": ldb.Dn(schema.ldb, names.schemadn).get_casefold(), 
-                "SCHEMADN_MOD2": ",objectguid",
                 "CONFIGDN": ldb.Dn(schema.ldb, names.configdn).get_casefold(),
                 "DOMAINDN": ldb.Dn(schema.ldb, names.domaindn).get_casefold(),
-                "SCHEMADN_MOD": "schema_data",
-                "CONFIGDN_MOD": "naming_fsmo",
-                "DOMAINDN_MOD": "pdc_fsmo",
-                "MODULES_LIST": ",".join(modules_list),
-                "TDB_MODULES_LIST": tdb_modules_list_as_string,
-                "MODULES_LIST2": ",".join(modules_list2),
-                "BACKEND_MOD": ",".join(backend_modules),
                 "LDAP_BACKEND_LINE": ldap_backend_line,
         })
 
         
-        samdb.load_ldif_file_add(setup_path("provision_init.ldif"))
+        setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
+                "BACKEND_TYPE": provision_backend.type,
+                "SERVER_ROLE": serverrole
+                })
 
         message("Setting up sam.ldb rootDSE")
         setup_samdb_rootdse(samdb, setup_path, names)
@@ -675,7 +572,7 @@ def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
 
         
 def secretsdb_self_join(secretsdb, domain, 
-                        netbiosname, domainsid, machinepass, 
+                        netbiosname, machinepass, domainsid=None,
                         realm=None, dnsdomain=None,
                         keytab_path=None, 
                         key_version_number=1,
@@ -710,7 +607,8 @@ def secretsdb_self_join(secretsdb, domain,
     msg["secret"] = [machinepass]
     msg["samAccountName"] = ["%s$" % netbiosname]
     msg["secureChannelType"] = [str(secure_channel_type)]
-    msg["objectSid"] = [ndr_pack(domainsid)]
+    if domainsid is not None:
+        msg["objectSid"] = [ndr_pack(domainsid)]
     
     res = secretsdb.search(base="cn=Primary Domains", 
                            attrs=attrs, 
@@ -956,8 +854,7 @@ def setup_samdb(path, setup_path, session_info, provision_backend, lp,
                            serverrole=serverrole, schema=schema)
 
     if (schema == None):
-        schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn,
-                        sambadn=names.sambadn)
+        schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn)
 
     # 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, 
@@ -1028,11 +925,6 @@ def setup_samdb(path, setup_path, session_info, provision_backend, lp,
             "CONFIGDN": names.configdn, 
             "DESCRIPTOR": descr,
             })
-        message("Modifying configuration container")
-        setup_modify_ldif(samdb, setup_path("provision_configuration_basedn_modify.ldif"), {
-            "CONFIGDN": names.configdn, 
-            "SCHEMADN": names.schemadn,
-            })
 
         # The LDIF here was created when the Schema object was constructed
         message("Setting up sam.ldb schema")
@@ -1085,6 +977,12 @@ def setup_samdb(path, setup_path, session_info, provision_backend, lp,
             "POLICYGUID_DC": policyguid_dc
             })
 
+        setup_modify_ldif(samdb, setup_path("provision_basedn_references.ldif"), {
+                "DOMAINDN": names.domaindn})
+
+        setup_modify_ldif(samdb, setup_path("provision_configuration_references.ldif"), {
+                "CONFIGDN": names.configdn,
+                "SCHEMADN": names.schemadn})
         if fill == FILL_FULL:
             message("Setting up sam.ldb users and groups")
             setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
@@ -1233,25 +1131,60 @@ def provision(setup_dir, message, session_info,
 
     if not os.path.exists(paths.private_dir):
         os.mkdir(paths.private_dir)
+    if not os.path.exists(os.path.join(paths.private_dir,"tls")):
+        os.mkdir(os.path.join(paths.private_dir,"tls"))
 
     ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
+    schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn)
     
-    schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn,
-                    sambadn=names.sambadn)
-    
-    provision_backend = ProvisionBackend(backend_type,
+    if backend_type == "ldb":
+        provision_backend = LDBBackend(backend_type,
                                          paths=paths, setup_path=setup_path,
                                          lp=lp, credentials=credentials, 
                                          names=names,
-                                         message=message, hostname=hostname,
-                                         root=root, schema=schema,
+                                         message=message)
+    elif backend_type == "existing":
+        provision_backend = ExistingBackend(backend_type,
+                                         paths=paths, setup_path=setup_path,
+                                         lp=lp, credentials=credentials, 
+                                         names=names,
+                                         message=message)
+    elif backend_type == "fedora-ds":
+        provision_backend = FDSBackend(backend_type,
+                                         paths=paths, setup_path=setup_path,
+                                         lp=lp, credentials=credentials, 
+                                         names=names,
+                                         message=message,
+                                         domainsid=domainsid,
+                                         schema=schema,
+                                         hostname=hostname,
                                          ldapadminpass=ldapadminpass,
+                                         slapd_path=slapd_path,
                                          ldap_backend_extra_port=ldap_backend_extra_port,
-                                         ol_mmr_urls=ol_mmr_urls, 
+                                         ldap_dryrun_mode=ldap_dryrun_mode,
+                                         root=root,
+                                         setup_ds_path=setup_ds_path)
+    elif backend_type == "openldap":
+        provision_backend = OpenLDAPBackend(backend_type,
+                                         paths=paths, setup_path=setup_path,
+                                         lp=lp, credentials=credentials, 
+                                         names=names,
+                                         message=message,
+                                         domainsid=domainsid,
+                                         schema=schema,
+                                         hostname=hostname,
+                                         ldapadminpass=ldapadminpass,
                                          slapd_path=slapd_path,
-                                         setup_ds_path=setup_ds_path,
+                                         ldap_backend_extra_port=ldap_backend_extra_port,
                                          ldap_dryrun_mode=ldap_dryrun_mode,
-                                         domainsid=domainsid)
+                                         ol_mmr_urls=ol_mmr_urls, 
+                                         nosync=nosync)
+    else:
+        raise ProvisioningError("Unknown LDAP backend type selected")
+
+    provision_backend.init()
+    provision_backend.start()
 
     # only install a new shares config db if there is none
     if not os.path.exists(paths.shareconf):
@@ -1333,16 +1266,15 @@ def provision(setup_dir, message, session_info,
         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":
-            secretsdb_self_join(secrets_ldb, domain=domain,
-                                realm=names.realm,
-                                dnsdomain=names.dnsdomain,
-                                netbiosname=names.netbiosname,
-                                domainsid=domainsid, 
-                                machinepass=machinepass,
-                                secure_channel_type=SEC_CHAN_BDC)
+        secretsdb_self_join(secrets_ldb, domain=names.domain,
+                            realm=names.realm,
+                            dnsdomain=names.dnsdomain,
+                            netbiosname=names.netbiosname,
+                            domainsid=domainsid, 
+                            machinepass=machinepass,
+                            secure_channel_type=SEC_CHAN_BDC)
 
+        if serverrole == "domain controller":
             secretsdb_setup_dns(secrets_ldb, setup_path, 
                                 realm=names.realm, dnsdomain=names.dnsdomain,
                                 dns_keytab_path=paths.dns_keytab,
@@ -1351,6 +1283,8 @@ def provision(setup_dir, message, session_info,
             domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
             assert isinstance(domainguid, str)
 
+            # Only make a zone file on the first DC, it should be replicated
+            # with DNS replication
             create_zone_file(paths.dns, setup_path, dnsdomain=names.dnsdomain,
                              hostip=hostip,
                              hostip6=hostip6, hostname=names.hostname,
@@ -1371,11 +1305,8 @@ 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)
 
-    if provision_backend.post_setup is not None:
-        provision_backend.post_setup()
-
-    if provision_backend.shutdown is not None:
-        provision_backend.shutdown()
+    provision_backend.post_setup()
+    provision_backend.shutdown()
     
     create_phpldapadmin_config(paths.phpldapadminconfig, setup_path, 
                                ldapi_url)
@@ -1392,7 +1323,7 @@ def provision(setup_dir, message, session_info,
     message("DNS Domain:            %s" % names.dnsdomain)
     message("DOMAIN SID:            %s" % str(domainsid))
     if samdb_fill == FILL_FULL:
-        message("Admin password:    %s" % adminpass)
+        message("Admin password:        %s" % adminpass)
     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())