samba-tool domain join: Refuse to re-join a DC with a still-valid password
authorAndrew Bartlett <abartlet@samba.org>
Tue, 31 May 2016 02:54:45 +0000 (14:54 +1200)
committerGarming Sam <garming@samba.org>
Thu, 16 Jun 2016 02:40:12 +0000 (04:40 +0200)
While the DC will eventually get back to the same state, it can take a
while, so try harder not to overwrite our already-working account

Signed-off-by: Andrew Bartlett <abartlet@samba.org>
Reviewed-by: Garming Sam <garming@catalyst.net.nz>
python/samba/join.py
python/samba/tests/samba_tool/join.py [new file with mode: 0644]
source4/selftest/tests.py

index 103e4d93a8b68db38942ec4ec861a1f4b3cf8d2e..3532a7f1e8f459db6da6a3cef57005337f89a62c 100644 (file)
@@ -170,6 +170,7 @@ class dc_join(object):
         ctx.managedby = None
         ctx.subdomain = False
         ctx.adminpass = None
+        ctx.partition_dn = None
 
     def del_noerror(ctx, dn, recursive=False):
         if recursive:
@@ -185,71 +186,97 @@ class dc_join(object):
         except Exception:
             pass
 
+    def cleanup_old_accounts(ctx):
+        res = ctx.samdb.search(base=ctx.samdb.get_default_basedn(),
+                               expression='sAMAccountName=%s' % ldb.binary_encode(ctx.samname),
+                               attrs=["msDS-krbTgtLink", "objectSID"])
+        if len(res) == 0:
+            return
+
+        creds = Credentials()
+        creds.guess(ctx.lp)
+        try:
+            creds.set_machine_account(ctx.lp)
+            machine_samdb = SamDB(url="ldap://%s" % ctx.server,
+                                  session_info=system_session(),
+                                credentials=creds, lp=ctx.lp)
+        except:
+            pass
+        else:
+            token_res = machine_samdb.search(scope=ldb.SCOPE_BASE, base="", attrs=["tokenGroups"])
+            if token_res[0]["tokenGroups"][0] \
+               == res[0]["objectSID"][0]:
+                raise DCJoinException("Not removing account %s which "
+                                   "looks like a Samba DC account "
+                                   "maching the password we already have.  "
+                                   "To override, remove secrets.ldb and secrets.tdb"
+                                % ctx.samname)
+
+        ctx.del_noerror(res[0].dn, recursive=True)
+
+        if "msDS-Krbtgtlink" in res[0]:
+            new_krbtgt_dn = res[0]["msDS-Krbtgtlink"][0]
+            del_noerror(ctx.new_krbtgt_dn)
+
+        res = ctx.samdb.search(base=ctx.samdb.get_default_basedn(),
+                               expression='(&(sAMAccountName=%s)(servicePrincipalName=%s))' %
+                               (ldb.binary_encode("dns-%s" % ctx.myname),
+                                ldb.binary_encode("dns/%s" % ctx.dnshostname)),
+                               attrs=[])
+        if res:
+            ctx.del_noerror(res[0].dn, recursive=True)
+
+        res = ctx.samdb.search(base=ctx.samdb.get_default_basedn(),
+                               expression='(sAMAccountName=%s)' % ldb.binary_encode("dns-%s" % ctx.myname),
+                            attrs=[])
+        if res:
+            raise DCJoinException("Not removing account %s which looks like "
+                               "a Samba DNS service account but does not "
+                               "have servicePrincipalName=%s" %
+                               (ldb.binary_encode("dns-%s" % ctx.myname),
+                                ldb.binary_encode("dns/%s" % ctx.dnshostname)))
+
+
     def cleanup_old_join(ctx):
         """Remove any DNs from a previous join."""
-        try:
-            # find the krbtgt link
-            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),
-                                       attrs=["msDS-krbTgtLink"])
-                if res:
-                    ctx.del_noerror(res[0].dn, recursive=True)
-
-                res = ctx.samdb.search(base=ctx.samdb.get_default_basedn(),
-                                       expression='(&(sAMAccountName=%s)(servicePrincipalName=%s))' % (ldb.binary_encode("dns-%s" % ctx.myname), ldb.binary_encode("dns/%s" % ctx.dnshostname)),
-                                       attrs=[])
-                if res:
-                    ctx.del_noerror(res[0].dn, recursive=True)
-
-                res = ctx.samdb.search(base=ctx.samdb.get_default_basedn(),
-                                       expression='(sAMAccountName=%s)' % ldb.binary_encode("dns-%s" % ctx.myname),
-                                       attrs=[])
-                if res:
-                    raise RuntimeError("Not removing account %s which looks like a Samba DNS service account but does not have servicePrincipalName=%s" % (ldb.binary_encode("dns-%s" % ctx.myname), ldb.binary_encode("dns/%s" % ctx.dnshostname)))
-
-            if ctx.connection_dn is not None:
-                ctx.del_noerror(ctx.connection_dn)
-            if ctx.krbtgt_dn is not None:
-                ctx.del_noerror(ctx.krbtgt_dn)
-            ctx.del_noerror(ctx.ntds_dn)
-            ctx.del_noerror(ctx.server_dn, recursive=True)
-            if ctx.topology_dn:
-                ctx.del_noerror(ctx.topology_dn)
-            if ctx.partition_dn:
-                ctx.del_noerror(ctx.partition_dn)
-            if res:
-                ctx.new_krbtgt_dn = res[0]["msDS-Krbtgtlink"][0]
-                ctx.del_noerror(ctx.new_krbtgt_dn)
-
-            if ctx.subdomain:
-                binding_options = "sign"
-                lsaconn = lsa.lsarpc("ncacn_ip_tcp:%s[%s]" % (ctx.server, binding_options),
-                                     ctx.lp, ctx.creds)
-
-                objectAttr = lsa.ObjectAttribute()
-                objectAttr.sec_qos = lsa.QosInfo()
-
-                pol_handle = lsaconn.OpenPolicy2(''.decode('utf-8'),
-                                                 objectAttr, security.SEC_FLAG_MAXIMUM_ALLOWED)
-
-                name = lsa.String()
-                name.string = ctx.realm
-                info = lsaconn.QueryTrustedDomainInfoByName(pol_handle, name, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
-
-                lsaconn.DeleteTrustedDomain(pol_handle, info.info_ex.sid)
-
-                name = lsa.String()
-                name.string = ctx.forest_domain_name
-                info = lsaconn.QueryTrustedDomainInfoByName(pol_handle, name, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
-
-                lsaconn.DeleteTrustedDomain(pol_handle, info.info_ex.sid)
+        # find the krbtgt link
+        if not ctx.subdomain:
+            ctx.cleanup_old_accounts()
+
+        if ctx.connection_dn is not None:
+            ctx.del_noerror(ctx.connection_dn)
+        if ctx.krbtgt_dn is not None:
+            ctx.del_noerror(ctx.krbtgt_dn)
+        ctx.del_noerror(ctx.ntds_dn)
+        ctx.del_noerror(ctx.server_dn, recursive=True)
+        if ctx.topology_dn:
+            ctx.del_noerror(ctx.topology_dn)
+        if ctx.partition_dn:
+            ctx.del_noerror(ctx.partition_dn)
+
+        if ctx.subdomain:
+            binding_options = "sign"
+            lsaconn = lsa.lsarpc("ncacn_ip_tcp:%s[%s]" % (ctx.server, binding_options),
+                                 ctx.lp, ctx.creds)
+
+            objectAttr = lsa.ObjectAttribute()
+            objectAttr.sec_qos = lsa.QosInfo()
+
+            pol_handle = lsaconn.OpenPolicy2(''.decode('utf-8'),
+                                             objectAttr, security.SEC_FLAG_MAXIMUM_ALLOWED)
+
+            name = lsa.String()
+            name.string = ctx.realm
+            info = lsaconn.QueryTrustedDomainInfoByName(pol_handle, name, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
+
+            lsaconn.DeleteTrustedDomain(pol_handle, info.info_ex.sid)
+
+            name = lsa.String()
+            name.string = ctx.forest_domain_name
+            info = lsaconn.QueryTrustedDomainInfoByName(pol_handle, name, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
+
+            lsaconn.DeleteTrustedDomain(pol_handle, info.info_ex.sid)
 
-        except Exception:
-            pass
 
     def promote_possible(ctx):
         """confirm that the account is just a bare NT4 BDC or a member server, so can be safely promoted"""
diff --git a/python/samba/tests/samba_tool/join.py b/python/samba/tests/samba_tool/join.py
new file mode 100644 (file)
index 0000000..1cc688f
--- /dev/null
@@ -0,0 +1,29 @@
+# Unix SMB/CIFS implementation.
+# Copyright (C) Andrew Bartlett <abartlet@samba.org> 2016
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+import os, ldb
+from samba.tests.samba_tool.base import SambaToolCmdTest
+
+class JoinCmdTestCase(SambaToolCmdTest):
+    """Test for samba-tool fsmo show subcommand"""
+
+    def test_rejoin(self):
+        """Run domain join to confirm it errors because we are already joined"""
+        (result, out, err) = self.runsubcmd("domain", "join", os.environ["REALM"], "dc", "-U%s%%%s" % (os.environ["USERNAME"], os.environ["PASSWORD"]))
+
+        self.assertCmdFail(result)
+        self.assertTrue("Not removing account" in err,"Should fail with exception")
index 8e92da345e64c01d213229ba4da48b1c01a076a2..cebdab7d889d64007794c4f97bb7bc649636c257 100755 (executable)
@@ -540,6 +540,7 @@ planpythontestsuite("ad_dc_ntvfs:local", "samba.tests.dcerpc.bare")
 planpythontestsuite("ad_dc_ntvfs:local", "samba.tests.dcerpc.unix")
 planpythontestsuite("ad_dc_ntvfs:local", "samba.tests.dcerpc.srvsvc")
 planpythontestsuite("ad_dc_ntvfs:local", "samba.tests.samba_tool.timecmd")
+planpythontestsuite("ad_dc_ntvfs:local", "samba.tests.samba_tool.join")
 
 # test fsmo show
 for env in ["ad_dc_ntvfs", "fl2000dc", "fl2003dc", "fl2008r2dc"]: