s4/scripting/bin: py2/py3 compatability always decode result of b64encode
[nivanova/samba-autobuild/.git] / source4 / scripting / bin / samba_upgradedns
index f57ff7296300dbd1d333fa0dd12b84678119c85e..e29ba7092b57bc5a49684dba9bb4716e65bf9d34 100755 (executable)
@@ -20,6 +20,7 @@
 
 import sys
 import os
+import errno
 import optparse
 import logging
 import grp
@@ -209,6 +210,36 @@ def import_zone_data(samdb, logger, zone, serial, domaindn, forestdn,
             raise
         logger.debug("Added DNS record %s" % (fqdn))
 
+def cleanup_remove_file(file_path):
+    try:
+        os.remove(file_path)
+    except OSError as e:
+        if e.errno not in [errno.EEXIST, errno.ENOENT]:
+            pass
+        else:
+            logger.debug("Could not remove %s: %s" % (file_path, e.strerror))
+
+def cleanup_remove_dir(dir_path):
+    try:
+        for root, dirs, files in os.walk(dir_path, topdown=False):
+            for name in files:
+                os.remove(os.path.join(root, name))
+            for name in dirs:
+                os.rmdir(os.path.join(root, name))
+        os.rmdir(dir_path)
+    except OSError as e:
+        if e.errno not in [errno.EEXIST, errno.ENOENT]:
+            pass
+        else:
+            logger.debug("Could not delete dir %s: %s" % (dir_path, e.strerror))
+
+def cleanup_obsolete_dns_files(paths):
+    cleanup_remove_file(os.path.join(paths.private_dir, "named.conf"))
+    cleanup_remove_file(os.path.join(paths.private_dir, "named.conf.update"))
+    cleanup_remove_file(os.path.join(paths.private_dir, "named.txt"))
+
+    cleanup_remove_dir(os.path.join(paths.private_dir, "dns"))
+
 
 # dnsprovision creates application partitions for AD based DNS mainly if the existing
 # provision was created using earlier snapshots of samba4 which did not have support
@@ -299,7 +330,7 @@ if __name__ == '__main__':
             zone = dns.zone.from_file(paths.dns, relativize=False)
             rrset = zone.get_rdataset("%s." % dnsdomain, dns.rdatatype.SOA)
             serial = int(rrset[0].serial)
-        except Exception, e:
+        except Exception as e:
             logger.warn("Error parsing DNS data from '%s' (%s)" % (paths.dns, str(e)))
             logger.warn("DNS records will be automatically created")
             autofill = True
@@ -411,15 +442,25 @@ if __name__ == '__main__':
 
     # Special stuff for DLZ backend
     if opts.dns_backend == "BIND9_DLZ":
+        config_migration = False
+
+        if (paths.private_dir != paths.binddns_dir and
+            os.path.isfile(os.path.join(paths.private_dir, "named.conf"))):
+            config_migration = True
+
         # Check if dns-HOSTNAME account exists and create it if required
         secrets_msgs = ldbs.secrets.search(expression='(samAccountName=dns-%s)' % hostname, attrs=['secret'])
-        if len(secrets_msgs) == 0:
+        msg = ldbs.sam.search(base=domaindn, scope=ldb.SCOPE_DEFAULT,
+                              expression='(sAMAccountName=dns-%s)' % (hostname),
+                              attrs=[])
 
+        if len(secrets_msgs) == 0 or len(msg) == 0:
             logger.info("Adding dns-%s account" % hostname)
 
-            msg = ldbs.sam.search(base=domaindn, scope=ldb.SCOPE_DEFAULT,
-                                  expression='(sAMAccountName=dns-%s)' % (hostname),
-                                  attrs=[])
+            if len(secrets_msgs) == 1:
+                dn = secrets_msgs[0].dn
+                ldbs.secrets.delete(dn)
+
             if len(msg) == 1:
                 dn = msg[0].dn
                 ldbs.sam.delete(dn)
@@ -428,7 +469,7 @@ if __name__ == '__main__':
             setup_add_ldif(ldbs.sam, setup_path("provision_dns_add_samba.ldif"), {
                     "DNSDOMAIN": dnsdomain,
                     "DOMAINDN": domaindn,
-                    "DNSPASS_B64": b64encode(dnspass.encode('utf-16-le')),
+                    "DNSPASS_B64": b64encode(dnspass.encode('utf-16-le')).decode('utf8'),
                     "HOSTNAME" : hostname,
                     "DNSNAME" : dnsname }
                            )
@@ -442,13 +483,50 @@ if __name__ == '__main__':
                 dns_key_version_number = None
 
             secretsdb_setup_dns(ldbs.secrets, names,
-                                paths.private_dir, realm=names.realm,
+                                paths.private_dir, paths.binddns_dir, realm=names.realm,
                                 dnsdomain=names.dnsdomain,
                                 dns_keytab_path=paths.dns_keytab, dnspass=dnspass,
                                 key_version_number=dns_key_version_number)
+
         else:
             logger.info("dns-%s account already exists" % hostname)
 
+        private_dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
+        bind_dns_keytab_path = os.path.join(paths.binddns_dir, paths.dns_keytab)
+
+        if os.path.isfile(private_dns_keytab_path):
+            if os.path.isfile(bind_dns_keytab_path):
+                try:
+                    os.unlink(bind_dns_keytab_path)
+                except OSError as e:
+                    logger.error("Failed to remove %s: %s" %
+                                 (bind_dns_keytab_path, e.strerror))
+
+            # link the dns.keytab to the bind-dns directory
+            try:
+                os.link(private_dns_keytab_path, bind_dns_keytab_path)
+            except OSError as e:
+                logger.error("Failed to create link %s -> %s: %s" %
+                             (private_dns_keytab_path, bind_dns_keytab_path, e.strerror))
+
+            # chown the dns.keytab in the bind-dns directory
+            if paths.bind_gid is not None:
+                try:
+                    os.chmod(paths.binddns_dir, 0o770)
+                    os.chown(paths.binddns_dir, -1, paths.bind_gid)
+                except OSError:
+                    if not os.environ.has_key('SAMBA_SELFTEST'):
+                        logger.info("Failed to chown %s to bind gid %u",
+                                    paths.binddns_dir, paths.bind_gid)
+                try:
+                    os.chmod(bind_dns_keytab_path, 0640)
+                    os.chown(bind_dns_keytab_path, -1, paths.bind_gid)
+                except OSError:
+                    if not os.environ.has_key('SAMBA_SELFTEST'):
+                        logger.info("Failed to chown %s to bind gid %u",
+                                    bind_dns_keytab_path, paths.bind_gid)
+
+
         # This forces a re-creation of dns directory and all the files within
         # It's an overkill, but it's easier to re-create a samdb copy, rather
         # than trying to fix a broken copy.
@@ -461,11 +539,37 @@ if __name__ == '__main__':
         create_named_conf(paths, names.realm, dnsdomain, opts.dns_backend, logger)
 
         create_named_txt(paths.namedtxt, names.realm, dnsdomain, dnsname,
-                         paths.private_dir, paths.dns_keytab)
-        logger.info("See %s for an example configuration include file for BIND", paths.namedconf)
-        logger.info("and %s for further documentation required for secure DNS "
-                    "updates", paths.namedtxt)
+                         paths.binddns_dir, paths.dns_keytab)
+
+        cleanup_obsolete_dns_files(paths)
+
+        if config_migration:
+            logger.info("ATTENTION: The BIND configuration and keytab has been moved to: %s",
+                        paths.binddns_dir)
+            logger.info("           Please update your BIND configuration accordingly.")
+        else:
+            logger.info("See %s for an example configuration include file for BIND", paths.namedconf)
+            logger.info("and %s for further documentation required for secure DNS "
+                        "updates", paths.namedtxt)
+
     elif opts.dns_backend == "SAMBA_INTERNAL":
+        # Make sure to remove everything from the bind-dns directory to avoid
+        # possible security issues with the named group having write access
+        # to all AD partions
+        cleanup_remove_file(os.path.join(paths.binddns_dir, "dns.keytab"))
+        cleanup_remove_file(os.path.join(paths.binddns_dir, "named.conf"))
+        cleanup_remove_file(os.path.join(paths.binddns_dir, "named.conf.update"))
+        cleanup_remove_file(os.path.join(paths.binddns_dir, "named.txt"))
+
+        cleanup_remove_dir(os.path.dirname(paths.dns))
+
+        try:
+            os.chmod(paths.private_dir, 0o700)
+            os.chown(paths.private_dir, -1, 0)
+        except:
+            logger.warn("Failed to restore owner and permissions for %s",
+                        (paths.private_dir))
+
         # Check if dns-HOSTNAME account exists and delete it if required
         try:
             dn_str = 'samAccountName=dns-%s,CN=Principals' % hostname