Merge branch 'master' of git://git.samba.org/samba
authorNadezhda Ivanova <nadezhda.ivanova@postpath.com>
Mon, 21 Sep 2009 21:26:15 +0000 (14:26 -0700)
committerNadezhda Ivanova <nadezhda.ivanova@postpath.com>
Mon, 21 Sep 2009 21:26:15 +0000 (14:26 -0700)
1  2 
source4/scripting/python/samba/provision.py

index 25cec4b143340ea01281014f69202663df34d7a4,9a41709830132f05c0ffacb36e63749c00257e1d..2d3e04eac1bdda1d2fb7d18ac4717599e766ec66
@@@ -44,7 -44,7 +44,7 @@@ from credentials import Credentials, DO
  from auth import system_session, admin_session
  from samba import version, Ldb, substitute_var, valid_netbios_name
  from samba import check_all_substituted
- from samba import DS_DOMAIN_FUNCTION_2000, DS_DC_FUNCTION_2008_R2
+ from samba import DS_DOMAIN_FUNCTION_2000, DS_DOMAIN_FUNCTION_2008, DS_DC_FUNCTION_2008, DS_DC_FUNCTION_2008_R2
  from samba.samdb import SamDB
  from samba.idmap import IDmapDB
  from samba.dcerpc import security
@@@ -54,14 -54,10 +54,10 @@@ from ldb import SCOPE_SUBTREE, SCOPE_ON
  from ms_schema import read_ms_schema
  from ms_display_specifiers import read_ms_ldif
  from signal import SIGTERM
+ from dcerpc.misc import SEC_CHAN_BDC, SEC_CHAN_WKSTA
  
  __docformat__ = "restructuredText"
  
- class ProvisioningError(ValueError):
-   pass
  def find_setup_dir():
      """Find the setup directory used by provision."""
      dirname = os.path.dirname(__file__)
          return ret
      raise Exception("Unable to find setup directory.")
  
 +def get_schema_descriptor(domain_sid):
 +    sddl = "O:SAG:SAD:(A;CI;RPLCLORC;;;AU)(A;CI;RPWPCRCCLCLORCWOWDSW;;;SA)" \
 +           "(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \
 +           "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
 +           "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
 +           "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
 +           "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
 +           "S:(AU;SA;WPCCDCWOWDSDDTSW;;;WD)" \
 +           "(AU;CISA;WP;;;WD)(AU;SA;CR;;;BA)" \
 +           "(AU;SA;CR;;;DU)(OU;SA;CR;e12b56b6-0a95-11d1-adbb-00c04fd8d5cd;;WD)" \
 +           "(OU;SA;CR;45ec5156-db7e-47bb-b53f-dbeb2d03c40f;;WD)"
 +    sec = security.descriptor.from_sddl(sddl, domain_sid)
 +    return b64encode(ndr_pack(sec))
 +
 +def get_config_descriptor(domain_sid):
 +    sddl = "O:EAG:EAD:(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
 +           "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
 +           "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
 +           "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
 +           "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
 +           "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
 +           "(A;;RPLCLORC;;;AU)(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
 +           "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;CIIO;RPWPCRCCLCLORCWOWDSDSW;;;DA)" \
 +           "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
 +           "(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)" \
 +           "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)
 +    return b64encode(ndr_pack(sec))
 +
  
  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):
@@@ -176,7 -144,7 +177,7 @@@ class ProvisionResult(object)
          self.samdb = None
          
  class Schema(object):
 -    def __init__(self, setup_path, schemadn=None, 
 +    def __init__(self, setup_path, domain_sid, schemadn=None,
                   serverdn=None, sambadn=None, ldap_backend_type=None):
          """Load schema for the SamDB from the AD schema files and samba4_schema.ldif
          
                                                    {"SCHEMADN": schemadn,
                                                     "SERVERDN": serverdn,
                                                     })
 +
 +        descr = get_schema_descriptor(domain_sid)
          self.schema_dn_add = read_and_sub_file(setup_path("provision_schema_basedn.ldif"),
 -                                               {"SCHEMADN": schemadn
 +                                               {"SCHEMADN": schemadn,
 +                                                "DESCRIPTOR": descr
                                                  })
  
          prefixmap = open(setup_path("prefixMap.txt"), 'r').read()
@@@ -355,7 -320,6 +356,6 @@@ def provision_paths_from_lp(lp, dnsdoma
      """
      paths = ProvisionPaths()
      paths.private_dir = lp.get("private dir")
-     paths.keytab = "secrets.keytab"
      paths.dns_keytab = "dns.keytab"
  
      paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
@@@ -695,26 -659,83 +695,83 @@@ def setup_samdb_partitions(samdb_path, 
  
      samdb.transaction_commit()
      
+ def secretsdb_self_join(secretsdb, domain, 
+                         netbiosname, domainsid, machinepass, 
+                         realm=None, dnsdomain=None,
+                         keytab_path=None, 
+                         key_version_number=1,
+                         secure_channel_type=SEC_CHAN_WKSTA):
+     """Add domain join-specific bits to a secrets database.
+     
+     :param secretsdb: Ldb Handle to the secrets database
+     :param machinepass: Machine password
+     """
+     attrs=["whenChanged",
+            "secret",
+            "priorSecret",
+            "priorChanged",
+            "krb5Keytab",
+            "privateKeytab"]
+     
+     msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain));
+     msg["secureChannelType"] = str(secure_channel_type)
+     msg["flatname"] = [domain]
+     msg["objectClass"] = ["top", "primaryDomain"]
+     if realm is not None:
+       if dnsdomain is None:
+         dnsdomain = realm.lower()
+       msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
+       msg["realm"] = realm
+       msg["saltPrincipal"] = "host/%s.%s@%s" % (netbiosname.lower(), dnsdomain.lower(), realm.upper())
+       msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
+       msg["privateKeytab"] = ["secrets.keytab"];
+     msg["secret"] = [machinepass]
+     msg["samAccountName"] = ["%s$" % netbiosname]
+     msg["secureChannelType"] = [str(secure_channel_type)]
+     msg["objectSid"] = [ndr_pack(domainsid)]
+     
+     res = secretsdb.search(base="cn=Primary Domains", 
+                            attrs=attrs, 
+                            expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain))" % (domain, realm, str(domainsid))), 
+                            scope=SCOPE_ONELEVEL)
+     
+     for del_msg in res:
+       if del_msg.dn is not msg.dn:
+         secretsdb.delete(del_msg.dn)
+     res = secretsdb.search(base=msg.dn, attrs=attrs, scope=SCOPE_BASE)
  
+     if len(res) == 1:
+       msg["priorSecret"] = res[0]["secret"]
+       msg["priorWhenChanged"] = res[0]["whenChanged"]
  
- def secretsdb_become_dc(secretsdb, setup_path, domain, realm, dnsdomain, 
-                         netbiosname, domainsid, keytab_path, samdb_url, 
-                         dns_keytab_path, dnspass, machinepass):
-     """Add DC-specific bits to a secrets database.
+       if res["privateKeytab"] is not None:
+         msg["privateKeytab"] = res[0]["privateKeytab"]
+       if res["krb5Keytab"] is not None:
+         msg["krb5Keytab"] = res[0]["krb5Keytab"]
+       for el in msg:
+         el.set_flags(ldb.FLAG_MOD_REPLACE)
+         secretsdb.modify(msg)
+     else:
+       secretsdb.add(msg)
+ def secretsdb_setup_dns(secretsdb, setup_path, realm, dnsdomain, 
+                         dns_keytab_path, dnspass):
+     """Add DNS 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,
+     setup_ldb(secretsdb, setup_path("secrets_dns.ldif"), { 
              "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),
              })
@@@ -738,6 -759,7 +795,7 @@@ def setup_secretsdb(path, setup_path, s
      secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
      secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
                        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():
@@@ -873,9 -895,10 +931,10 @@@ def setup_samdb(path, setup_path, sessi
      :note: This will wipe the main SAM database file!
      """
  
-     domainFunctionality = DS_DOMAIN_FUNCTION_2000
-     forestFunctionality = DS_DOMAIN_FUNCTION_2000
-     domainControllerFunctionality = DS_DC_FUNCTION_2008_R2
+     # Do NOT change these default values without discussion with the team and reslease manager.  
+     domainFunctionality = DS_DOMAIN_FUNCTION_2008
+     forestFunctionality = DS_DOMAIN_FUNCTION_2008
+     domainControllerFunctionality = DS_DC_FUNCTION_2008
  
      # Also wipes the database
      setup_samdb_partitions(path, setup_path, message=message, lp=lp,
                             ldap_backend=ldap_backend, serverrole=serverrole)
  
      if (schema == None):
 -        schema = Schema(setup_path, schemadn=names.schemadn, serverdn=names.serverdn,
 +        schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn,
              sambadn=names.sambadn, ldap_backend_type=ldap_backend.ldap_backend_type)
  
      # Load the database, but importantly, use Ldb not SamDB as we don't want to load the global schema
              })
  
          message("Adding configuration container")
 +        descr = get_config_descriptor(domainsid);
          setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
              "CONFIGDN": names.configdn, 
 +            "DESCRIPTOR": descr,
              })
          message("Modifying configuration container")
          setup_modify_ldif(samdb, setup_path("provision_configuration_basedn_modify.ldif"), {
@@@ -1088,7 -1109,7 +1147,7 @@@ def provision(setup_dir, message, sessi
      """
  
      def setup_path(file):
 -        return os.path.join(setup_dir, file)
 +      return os.path.join(setup_dir, file)
  
      if domainsid is None:
        domainsid = security.random_sid()
  
      ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
      
 -    schema = Schema(setup_path, schemadn=names.schemadn, serverdn=names.serverdn,
 +    schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn,
          sambadn=names.sambadn, ldap_backend_type=ldap_backend_type)
      
      secrets_credentials = credentials
  
          # Only make a zone file on the first DC, it should be replicated with DNS replication
          if serverrole == "domain controller":
-             secrets_ldb = Ldb(paths.secrets, session_info=session_info, 
-                               credentials=credentials, lp=lp)
-             secretsdb_become_dc(secrets_ldb, setup_path, domain=domain,
+             secretsdb_self_join(secrets_ldb, domain=domain,
                                  realm=names.realm,
+                                 dnsdomain=names.dnsdomain,
                                  netbiosname=names.netbiosname,
                                  domainsid=domainsid, 
-                                 keytab_path=paths.keytab, samdb_url=paths.samdb,
+                                 machinepass=machinepass,
+                                 secure_channel_type=SEC_CHAN_BDC)
+             secretsdb_setup_dns(secrets_ldb, setup_path, 
+                                 realm=names.realm, dnsdomain=names.dnsdomain,
                                  dns_keytab_path=paths.dns_keytab,
-                                 dnspass=dnspass, machinepass=machinepass,
-                                 dnsdomain=names.dnsdomain)
+                                 dnspass=dnspass)
  
              domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
              assert isinstance(domainguid, str)
                               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":