samba-tool: skip chown in sysvolreset when it would fail on a GID
authorAndrew Bartlett <abartlet@samba.org>
Fri, 5 Oct 2012 00:19:17 +0000 (10:19 +1000)
committerAndrew Bartlett <abartlet@samba.org>
Tue, 9 Oct 2012 13:24:44 +0000 (15:24 +0200)
This skips the chown of the files if (for example) the domain Admins group
were to own the file and not be able to because the group maps only to a GID.

This essentially papers over the problem, but may be enough to get us past
the Samba 4.0 release.

Andrew Bartlett

Autobuild-User(master): Andrew Bartlett <abartlet@samba.org>
Autobuild-Date(master): Tue Oct  9 15:24:44 CEST 2012 on sn-devel-104

source4/scripting/python/samba/ntacls.py
source4/scripting/python/samba/provision/__init__.py

index 2108a6432d4fa975e0e45b6a174d613e20d2fe40..44cbbe95591b1994cd61cc35f22ea04b7f405787 100644 (file)
@@ -21,7 +21,7 @@
 
 import os
 import samba.xattr_native, samba.xattr_tdb, samba.posix_eadb
-from samba.dcerpc import security, xattr
+from samba.dcerpc import security, xattr, idmap
 from samba.ndr import ndr_pack, ndr_unpack
 from samba.samba3 import smbd
 
@@ -82,10 +82,43 @@ def getntacl(lp, file, backend=None, eadbfile=None, direct_db_access=True):
         return smbd.get_nt_acl(file, security.SECINFO_OWNER | security.SECINFO_GROUP | security.SECINFO_DACL | security.SECINFO_SACL)
 
 
-def setntacl(lp, file, sddl, domsid, backend=None, eadbfile=None, use_ntvfs=True):
+def setntacl(lp, file, sddl, domsid, backend=None, eadbfile=None, use_ntvfs=True, skip_invalid_chown=False, passdb=None):
     sid = security.dom_sid(domsid)
     sd = security.descriptor.from_sddl(sddl, sid)
 
+    if not use_ntvfs and skip_invalid_chown:
+        # Check if the owner can be resolved as a UID
+        (owner_id, owner_type) = passdb.sid_to_id(sd.owner_sid)
+        if ((owner_type != idmap.ID_TYPE_UID) and (owner_type != idmap.ID_TYPE_BOTH)):
+            # Check if this particular owner SID was domain admins,
+            # because we special-case this as mapping to
+            # 'administrator' instead.
+            if sd.owner_sid == security.dom_sid("%s-%d" % (domsid, security.DOMAIN_RID_ADMINS)):
+                administrator = security.dom_sid("%s-%d" % (domsid, security.DOMAIN_RID_ADMINISTRATOR))
+                (admin_id, admin_type) = passdb.sid_to_id(administrator)
+
+                # Confirm we have a UID for administrator
+                if ((admin_type == idmap.ID_TYPE_UID) or (admin_type == idmap.ID_TYPE_BOTH)):
+
+                    # Set it, changing the owner to 'administrator' rather than domain admins
+                    sd2 = security.descriptor.from_sddl(sddl, sid)
+                    sd2.owner_sid = administrator
+
+                    smbd.set_nt_acl(file, security.SECINFO_OWNER |security.SECINFO_GROUP | security.SECINFO_DACL | security.SECINFO_SACL, sd2)
+
+                    # and then set an NTVFS ACL (which does not set the posix ACL) to pretend the owner really was set
+                    use_ntvfs = True
+                else:
+                    raise XattrBackendError("Unable to find UID for domain administrator %s, got id %d of type %d" % (administrator, admin_id, admin_type))
+            else:
+                # For all other owning users, reset the owner to root
+                # and then set the ACL without changing the owner
+                #
+                # This won't work in test environments, as it tries a real (rather than xattr-based fake) chown
+
+                os.chown(file, 0, 0)
+                smbd.set_nt_acl(file, security.SECINFO_GROUP | security.SECINFO_DACL | security.SECINFO_SACL, sd)
+
     if use_ntvfs:
         (backend_obj, dbname) = checkset_backend(lp, backend, eadbfile)
         ntacl = xattr.NTACL()
index d5d57d216493761e7d2c99186fe435c0f8b48168..9966192a1973594656fb8669b98076dcb71ee673 100644 (file)
@@ -1365,18 +1365,18 @@ SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI
 POLICIES_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)(A;OICI;0x001301bf;;;PA)"
 
 
-def set_dir_acl(path, acl, lp, domsid, use_ntvfs):
-    setntacl(lp, path, acl, domsid, use_ntvfs=use_ntvfs)
+def set_dir_acl(path, acl, lp, domsid, use_ntvfs, passdb):
+    setntacl(lp, path, acl, domsid, use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb)
     for root, dirs, files in os.walk(path, topdown=False):
         for name in files:
             setntacl(lp, os.path.join(root, name), acl, domsid,
-                    use_ntvfs=use_ntvfs)
+                    use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb)
         for name in dirs:
             setntacl(lp, os.path.join(root, name), acl, domsid,
-                    use_ntvfs=use_ntvfs)
+                    use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb)
 
 
-def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs):
+def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb):
     """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
     folders beneath.
 
@@ -1391,7 +1391,7 @@ def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs):
     # Set ACL for GPO root folder
     root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
     setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid),
-            use_ntvfs=use_ntvfs)
+            use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb)
 
     res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
                         attrs=["cn", "nTSecurityDescriptor"],
@@ -1402,7 +1402,8 @@ def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs):
                          str(policy["nTSecurityDescriptor"])).as_sddl()
         policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
         set_dir_acl(policy_path, dsacl2fsacl(acl, str(domainsid)), lp,
-                    str(domainsid), use_ntvfs)
+                    str(domainsid), use_ntvfs,
+                    passdb=passdb)
 
 
 def setsysvolacl(samdb, netlogon, sysvol, uid, gid, domainsid, dnsdomain,
@@ -1418,6 +1419,7 @@ def setsysvolacl(samdb, netlogon, sysvol, uid, gid, domainsid, dnsdomain,
     :param dnsdomain: The DNS name of the domain
     :param domaindn: The DN of the domain (ie. DC=...)
     """
+    s4_passdb = None
 
     if not use_ntvfs:
         # This will ensure that the smbd code we are running when setting ACLs
@@ -1453,19 +1455,19 @@ def setsysvolacl(samdb, netlogon, sysvol, uid, gid, domainsid, dnsdomain,
         canchown = True
 
     # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
-    setntacl(lp,sysvol, SYSVOL_ACL, str(domainsid), use_ntvfs=use_ntvfs)
+    setntacl(lp,sysvol, SYSVOL_ACL, str(domainsid), use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=s4_passdb)
     for root, dirs, files in os.walk(sysvol, topdown=False):
         for name in files:
             if use_ntvfs and canchown:
                 os.chown(os.path.join(root, name), -1, gid)
-            setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid), use_ntvfs=use_ntvfs)
+            setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid), use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=s4_passdb)
         for name in dirs:
             if use_ntvfs and canchown:
                 os.chown(os.path.join(root, name), -1, gid)
-            setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid), use_ntvfs=use_ntvfs)
+            setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid), use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=s4_passdb)
 
     # Set acls on Policy folder and policies folders
-    set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs)
+    set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb=s4_passdb)
 
 def acl_type(direct_db_access):
     if direct_db_access: