s4-join: Setup correct DNS configuration
[ddiss/samba.git] / source4 / scripting / python / samba / join.py
index b46b4d8f069303ed864e6691507de60a45cf33c5..9ef7d3dd1737658719ae69fa7efa2bedd35efac3 100644 (file)
@@ -1,5 +1,3 @@
-#!/usr/bin/env python
-#
 # python join code
 # Copyright Andrew Tridgell 2010
 # Copyright Andrew Bartlett 2010
@@ -30,6 +28,7 @@ from samba.credentials import Credentials, DONT_USE_KERBEROS
 from samba.provision import secretsdb_self_join, provision, provision_fill, FILL_DRS, FILL_SUBDOMAIN
 from samba.schema import Schema
 from samba.net import Net
+from samba.provision.sambadns import setup_bind9_dns
 import logging
 import talloc
 import random
@@ -49,12 +48,20 @@ class dc_join(object):
 
     def __init__(ctx, server=None, creds=None, lp=None, site=None,
             netbios_name=None, targetdir=None, domain=None,
-            machinepass=None):
+            machinepass=None, use_ntvfs=False, dns_backend=None):
         ctx.creds = creds
         ctx.lp = lp
         ctx.site = site
         ctx.netbios_name = netbios_name
         ctx.targetdir = targetdir
+        ctx.use_ntvfs = use_ntvfs
+        if dns_backend is None:
+            ctx.dns_backend = "NONE"
+        else:
+            ctx.dns_backend = dns_backend
+
+        ctx.nc_list = []
+        ctx.full_nc_list = []
 
         ctx.creds.set_gensec_features(creds.get_gensec_features() | gensec.FEATURE_SEAL)
         ctx.net = Net(creds=ctx.creds, lp=ctx.lp)
@@ -147,12 +154,12 @@ class dc_join(object):
         '''remove any DNs from a previous join'''
         try:
             # find the krbtgt link
-            print("checking samaccountname")
+            print("checking sAMAccountName")
             if ctx.subdomain:
                 res = None
             else:
                 res = ctx.samdb.search(base=ctx.samdb.get_default_basedn(),
-                                       expression='samAccountName=%s' % ldb.binary_encode(ctx.samname),
+                                       expression='sAMAccountName=%s' % ldb.binary_encode(ctx.samname),
                                        attrs=["msDS-krbTgtLink"])
                 if res:
                     ctx.del_noerror(res[0].dn, recursive=True)
@@ -396,21 +403,21 @@ class dc_join(object):
         nc_list = [ ctx.base_dn, ctx.config_dn, ctx.schema_dn ]
 
         if ctx.behavior_version >= samba.dsdb.DS_DOMAIN_FUNCTION_2003:
-            rec["msDS-Behavior-Version"] = str(ctx.behavior_version)
+            rec["msDS-Behavior-Version"] = str(samba.dsdb.DS_DOMAIN_FUNCTION_2008_R2)
 
         if ctx.behavior_version >= samba.dsdb.DS_DOMAIN_FUNCTION_2003:
             rec["msDS-HasDomainNCs"] = ctx.base_dn
 
         if ctx.RODC:
             rec["objectCategory"] = "CN=NTDS-DSA-RO,%s" % ctx.schema_dn
-            rec["msDS-HasFullReplicaNCs"] = nc_list
+            rec["msDS-HasFullReplicaNCs"] = ctx.nc_list
             rec["options"] = "37"
             ctx.samdb.add(rec, ["rodc_join:1:1"])
         else:
             rec["objectCategory"] = "CN=NTDS-DSA,%s" % ctx.schema_dn
             rec["HasMasterNCs"]      = nc_list
             if ctx.behavior_version >= samba.dsdb.DS_DOMAIN_FUNCTION_2003:
-                rec["msDS-HasMasterNCs"] = nc_list
+                rec["msDS-HasMasterNCs"] = ctx.nc_list
             rec["options"] = "1"
             rec["invocationId"] = ndr_pack(ctx.invocation_id)
             ctx.DsAddEntry([rec])
@@ -487,13 +494,31 @@ class dc_join(object):
                                                            "servicePrincipalName")
             ctx.samdb.modify(m)
 
+            # The account password set operation should normally be done over
+            # LDAP. Windows 2000 DCs however allow this only with SSL
+            # connections which are hard to set up and otherwise refuse with
+            # ERR_UNWILLING_TO_PERFORM. In this case we fall back to libnet
+            # over SAMR.
             print "Setting account password for %s" % ctx.samname
-            ctx.samdb.setpassword("(&(objectClass=user)(sAMAccountName=%s))" % ldb.binary_encode(ctx.samname),
-                                  ctx.acct_pass,
-                                  force_change_at_next_login=False,
-                                  username=ctx.samname)
-            res = ctx.samdb.search(base=ctx.acct_dn, scope=ldb.SCOPE_BASE, attrs=["msDS-keyVersionNumber"])
-            ctx.key_version_number = int(res[0]["msDS-keyVersionNumber"][0])
+            try:
+                ctx.samdb.setpassword("(&(objectClass=user)(sAMAccountName=%s))"
+                                      % ldb.binary_encode(ctx.samname),
+                                      ctx.acct_pass,
+                                      force_change_at_next_login=False,
+                                      username=ctx.samname)
+            except ldb.LdbError, (num, _):
+                if num != ldb.ERR_UNWILLING_TO_PERFORM:
+                    pass
+                ctx.net.set_password(account_name=ctx.samname,
+                                     domain_name=ctx.domain_name,
+                                     newpassword=ctx.acct_pass)
+
+            res = ctx.samdb.search(base=ctx.acct_dn, scope=ldb.SCOPE_BASE,
+                                   attrs=["msDS-KeyVersionNumber"])
+            if "msDS-KeyVersionNumber" in res[0]:
+                ctx.key_version_number = int(res[0]["msDS-KeyVersionNumber"][0])
+            else:
+                ctx.key_version_number = None
 
             print("Enabling account")
             m = ldb.Message()
@@ -538,7 +563,7 @@ class dc_join(object):
         rec2["objectCategory"] = "CN=NTDS-DSA,%s" % ctx.schema_dn
         rec2["HasMasterNCs"]      = nc_list
         if ctx.behavior_version >= samba.dsdb.DS_DOMAIN_FUNCTION_2003:
-            rec2["msDS-HasMasterNCs"] = nc_list
+            rec2["msDS-HasMasterNCs"] = ctx.nc_list
         rec2["options"] = "1"
         rec2["invocationId"] = ndr_pack(ctx.invocation_id)
 
@@ -571,16 +596,15 @@ class dc_join(object):
         logger.addHandler(logging.StreamHandler(sys.stdout))
         smbconf = ctx.lp.configfile
 
-        presult = provision(logger, system_session(), None,
-                            smbconf=smbconf, targetdir=ctx.targetdir, samdb_fill=FILL_DRS,
-                            realm=ctx.realm, rootdn=ctx.root_dn, domaindn=ctx.base_dn,
-                            schemadn=ctx.schema_dn,
-                            configdn=ctx.config_dn,
-                            serverdn=ctx.server_dn, domain=ctx.domain_name,
-                            hostname=ctx.myname, domainsid=ctx.domsid,
-                            machinepass=ctx.acct_pass, serverrole="domain controller",
-                            sitename=ctx.site, lp=ctx.lp, ntdsguid=ctx.ntds_guid,
-                            dns_backend="NONE")
+        presult = provision(logger, system_session(), None, smbconf=smbconf,
+                targetdir=ctx.targetdir, samdb_fill=FILL_DRS, realm=ctx.realm,
+                rootdn=ctx.root_dn, domaindn=ctx.base_dn,
+                schemadn=ctx.schema_dn, configdn=ctx.config_dn,
+                serverdn=ctx.server_dn, domain=ctx.domain_name,
+                hostname=ctx.myname, domainsid=ctx.domsid,
+                machinepass=ctx.acct_pass, serverrole="domain controller",
+                sitename=ctx.site, lp=ctx.lp, ntdsguid=ctx.ntds_guid,
+                use_ntvfs=ctx.use_ntvfs, dns_backend=ctx.dns_backend)
         print "Provision OK for domain DN %s" % presult.domaindn
         ctx.local_samdb = presult.samdb
         ctx.lp          = presult.lp
@@ -619,7 +643,7 @@ class dc_join(object):
                                  targetdir=ctx.targetdir, samdb_fill=FILL_SUBDOMAIN,
                                  machinepass=ctx.acct_pass, serverrole="domain controller",
                                  lp=ctx.lp, hostip=ctx.names.hostip, hostip6=ctx.names.hostip6,
-                                 dns_backend="BIND9_DLZ")
+                                 dns_backend=ctx.dns_backend)
         print("Provision OK for domain %s" % ctx.names.dnsdomain)
 
     def join_replicate(ctx):
@@ -658,9 +682,30 @@ class dc_join(object):
                     destination_dsa_guid, rodc=ctx.RODC,
                     replica_flags=ctx.replica_flags)
             if not ctx.subdomain:
+                # Replicate first the critical object for the basedn
+                if not ctx.domain_replica_flags & drsuapi.DRSUAPI_DRS_CRITICAL_ONLY:
+                    print "Replicating critical objects from the base DN of the domain"
+                    ctx.domain_replica_flags |= drsuapi.DRSUAPI_DRS_CRITICAL_ONLY | drsuapi.DRSUAPI_DRS_GET_ANC
+                    repl.replicate(ctx.base_dn, source_dsa_invocation_id,
+                                destination_dsa_guid, rodc=ctx.RODC,
+                                replica_flags=ctx.domain_replica_flags)
+                    ctx.domain_replica_flags ^= drsuapi.DRSUAPI_DRS_CRITICAL_ONLY | drsuapi.DRSUAPI_DRS_GET_ANC
+                else:
+                    ctx.domain_replica_flags |= drsuapi.DRSUAPI_DRS_GET_ANC
                 repl.replicate(ctx.base_dn, source_dsa_invocation_id,
                                destination_dsa_guid, rodc=ctx.RODC,
                                replica_flags=ctx.domain_replica_flags)
+
+            if 'DC=DomainDnsZones,%s' % ctx.base_dn in ctx.nc_list:
+                repl.replicate('DC=DomainDnsZones,%s' % ctx.base_dn, source_dsa_invocation_id,
+                               destination_dsa_guid, rodc=ctx.RODC,
+                               replica_flags=ctx.replica_flags)
+
+            if 'DC=ForestDnsZones,%s' % ctx.root_dn in ctx.nc_list:
+                repl.replicate('DC=ForestDnsZones,%s' % ctx.root_dn, source_dsa_invocation_id,
+                               destination_dsa_guid, rodc=ctx.RODC,
+                               replica_flags=ctx.replica_flags)
+
             if ctx.RODC:
                 repl.replicate(ctx.acct_dn, source_dsa_invocation_id,
                         destination_dsa_guid,
@@ -697,10 +742,12 @@ class dc_join(object):
     def join_finalise(ctx):
         '''finalise the join, mark us synchronised and setup secrets db'''
 
+        logger = logging.getLogger("provision")
+        logger.addHandler(logging.StreamHandler(sys.stdout))
+
         print "Sending DsReplicateUpdateRefs for all the partitions"
-        ctx.send_DsReplicaUpdateRefs(ctx.schema_dn)
-        ctx.send_DsReplicaUpdateRefs(ctx.config_dn)
-        ctx.send_DsReplicaUpdateRefs(ctx.base_dn)
+        for nc in ctx.full_nc_list:
+            ctx.send_DsReplicaUpdateRefs(nc)
 
         print "Setting isSynchronized and dsServiceName"
         m = ldb.Message()
@@ -725,6 +772,15 @@ class dc_join(object):
                             secure_channel_type=ctx.secure_channel_type,
                             key_version_number=ctx.key_version_number)
 
+        if ctx.dns_backend.startswith("BIND9_"):
+            dnspass = samba.generate_random_password(128, 255)
+
+            setup_bind9_dns(ctx.local_samdb, secrets_ldb, security.dom_sid(ctx.domsid),
+                            ctx.names, ctx.paths, ctx.lp, logger,
+                            dns_backend=ctx.dns_backend,
+                            dnspass=dnspass, os_level=ctx.behavior_version,
+                            targetdir=ctx.targetdir)
+
     def join_setup_trusts(ctx):
         '''provision the local SAM'''
 
@@ -839,6 +895,20 @@ class dc_join(object):
 
 
     def do_join(ctx):
+        ctx.nc_list = [ ctx.config_dn, ctx.schema_dn ]
+        ctx.full_nc_list = [ctx.base_dn, ctx.config_dn, ctx.schema_dn ]
+
+        if not ctx.subdomain:
+            ctx.nc_list += [ctx.base_dn]
+            if ctx.dns_backend != "NONE":
+                ctx.nc_list += ['DC=DomainDnsZones,%s' % ctx.base_dn]
+
+        if ctx.dns_backend != "NONE":
+            ctx.full_nc_list += ['DC=DomainDnsZones,%s' % ctx.base_dn]
+            ctx.full_nc_list += ['DC=ForestDnsZones,%s' % ctx.root_dn]
+            ctx.nc_list += ['DC=ForestDnsZones,%s' % ctx.root_dn]
+
+
         ctx.cleanup_old_join()
         try:
             ctx.join_add_objects()
@@ -849,7 +919,7 @@ class dc_join(object):
                 ctx.join_provision_own_domain()
                 ctx.join_setup_trusts()
             ctx.join_finalise()
-        except Exception:
+        except:
             print "Join failed - cleaning up"
             ctx.cleanup_old_join()
             raise
@@ -857,11 +927,11 @@ class dc_join(object):
 
 def join_RODC(server=None, creds=None, lp=None, site=None, netbios_name=None,
               targetdir=None, domain=None, domain_critical_only=False,
-              machinepass=None):
+              machinepass=None, use_ntvfs=False, dns_backend=None):
     """join as a RODC"""
 
     ctx = dc_join(server, creds, lp, site, netbios_name, targetdir, domain,
-                  machinepass)
+                  machinepass, use_ntvfs, dns_backend)
 
     lp.set("workgroup", ctx.domain_name)
     print("workgroup is %s" % ctx.domain_name)
@@ -911,10 +981,10 @@ def join_RODC(server=None, creds=None, lp=None, site=None, netbios_name=None,
 
 def join_DC(server=None, creds=None, lp=None, site=None, netbios_name=None,
             targetdir=None, domain=None, domain_critical_only=False,
-            machinepass=None):
+            machinepass=None, use_ntvfs=False, dns_backend=None):
     """join as a DC"""
     ctx = dc_join(server, creds, lp, site, netbios_name, targetdir, domain,
-                  machinepass)
+                  machinepass, use_ntvfs, dns_backend)
 
     lp.set("workgroup", ctx.domain_name)
     print("workgroup is %s" % ctx.domain_name)
@@ -941,10 +1011,10 @@ def join_DC(server=None, creds=None, lp=None, site=None, netbios_name=None,
 
 def join_subdomain(server=None, creds=None, lp=None, site=None, netbios_name=None,
                    targetdir=None, parent_domain=None, dnsdomain=None, netbios_domain=None,
-                   machinepass=None):
+                   machinepass=None, use_ntvfs=False, dns_backend=None):
     """join as a DC"""
     ctx = dc_join(server, creds, lp, site, netbios_name, targetdir, parent_domain,
-                  machinepass)
+                  machinepass, use_ntvfs, dns_backend)
     ctx.subdomain = True
     ctx.parent_domain_name = ctx.domain_name
     ctx.domain_name = netbios_domain