r4595: on create check access against parent not child ...
[samba.git] / source4 / ntvfs / posix / pvfs_acl.c
index 6741fb851d27e56f2165b6a803a72f4ce3090d5b..590c9c18b504636301373b015dbbfb1154586f27 100644 (file)
@@ -185,12 +185,15 @@ static void normalise_sd_flags(struct security_descriptor *sd, uint32_t secinfo_
 NTSTATUS pvfs_acl_set(struct pvfs_state *pvfs, 
                      struct smbsrv_request *req,
                      struct pvfs_filename *name, int fd, 
+                     uint32_t access_mask,
                      union smb_setfileinfo *info)
 {
        struct xattr_NTACL *acl;
        uint32_t secinfo_flags = info->set_secdesc.in.secinfo_flags;
-       struct security_descriptor *new_sd, *sd;
+       struct security_descriptor *new_sd, *sd, orig_sd;
        NTSTATUS status;
+       uid_t uid = -1;
+       gid_t gid = -1;
 
        acl = talloc_p(req, struct xattr_NTACL);
        if (acl == NULL) {
@@ -214,13 +217,30 @@ NTSTATUS pvfs_acl_set(struct pvfs_state *pvfs,
        }
 
        new_sd = info->set_secdesc.in.sd;
+       orig_sd = *sd;
+
+       uid = name->st.st_uid;
+       gid = name->st.st_gid;
 
        /* only set the elements that have been specified */
-       if (secinfo_flags & SECINFO_OWNER) {
+       if ((secinfo_flags & SECINFO_OWNER) && 
+           !dom_sid_equal(sd->owner_sid, new_sd->owner_sid)) {
+               if (!(access_mask & SEC_STD_WRITE_OWNER)) {
+                       return NT_STATUS_ACCESS_DENIED;
+               }
                sd->owner_sid = new_sd->owner_sid;
+               status = sidmap_sid_to_unixuid(pvfs->sidmap, sd->owner_sid, &uid);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return status;
+               }
        }
-       if (secinfo_flags & SECINFO_GROUP) {
+       if ((secinfo_flags & SECINFO_GROUP) &&
+           !dom_sid_equal(sd->group_sid, new_sd->group_sid)) {
                sd->group_sid = new_sd->group_sid;
+               status = sidmap_sid_to_unixgid(pvfs->sidmap, sd->owner_sid, &gid);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return status;
+               }
        }
        if (secinfo_flags & SECINFO_DACL) {
                sd->dacl = new_sd->dacl;
@@ -228,10 +248,30 @@ NTSTATUS pvfs_acl_set(struct pvfs_state *pvfs,
        }
        if (secinfo_flags & SECINFO_SACL) {
                sd->sacl = new_sd->sacl;
+               if (!(access_mask & SEC_FLAG_SYSTEM_SECURITY)) {
+                       return NT_STATUS_ACCESS_DENIED;
+               }
                pvfs_translate_generic_bits(sd->sacl);
        }
 
-       status = pvfs_acl_save(pvfs, name, fd, acl);
+       if (uid != -1 || gid != -1) {
+               int ret;
+               if (fd == -1) {
+                       ret = chown(name->full_name, uid, gid);
+               } else {
+                       ret = fchown(fd, uid, gid);
+               }
+               if (ret == -1) {
+                       return pvfs_map_errno(pvfs, errno);
+               }
+       }
+
+       /* we avoid saving if the sd is the same. This means when clients
+          copy files and end up copying the default sd that we don't
+          needlessly use xattrs */
+       if (!security_descriptor_equal(sd, &orig_sd)) {
+               status = pvfs_acl_save(pvfs, name, fd, acl);
+       }
 
        return status;
 }
@@ -306,6 +346,8 @@ NTSTATUS pvfs_access_check_unix(struct pvfs_state *pvfs,
                return NT_STATUS_ACCESS_DENIED;
        }
 
+       *access_mask |= SEC_FILE_READ_ATTRIBUTE;
+
        return NT_STATUS_OK;
 }
 
@@ -350,6 +392,8 @@ NTSTATUS pvfs_access_check(struct pvfs_state *pvfs,
        /* expand the generic access bits to file specific bits */
        *access_mask = pvfs_translate_mask(*access_mask);
 
+       *access_mask &= ~SEC_FILE_READ_ATTRIBUTE;
+
        /* check the acl against the required access mask */
        status = sec_access_check(sd, token, *access_mask, access_mask);
 
@@ -382,7 +426,35 @@ NTSTATUS pvfs_access_check_simple(struct pvfs_state *pvfs,
 */
 NTSTATUS pvfs_access_check_create(struct pvfs_state *pvfs, 
                                  struct smbsrv_request *req,
-                                 struct pvfs_filename *name)
+                                 struct pvfs_filename *name,
+                                 uint32_t *access_mask)
+{
+       struct pvfs_filename *parent;
+       NTSTATUS status;
+
+       status = pvfs_resolve_parent(pvfs, req, name, &parent);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       status = pvfs_access_check(pvfs, req, parent, access_mask);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       if (! ((*access_mask) & SEC_DIR_ADD_FILE)) {
+               return pvfs_access_check_simple(pvfs, req, parent, SEC_DIR_ADD_FILE);
+       }
+
+       return status;
+}
+
+/*
+  access check for creating a new file/directory - no access mask supplied
+*/
+NTSTATUS pvfs_access_check_create_nomask(struct pvfs_state *pvfs, 
+                                        struct smbsrv_request *req,
+                                        struct pvfs_filename *name)
 {
        struct pvfs_filename *parent;
        NTSTATUS status;
@@ -434,11 +506,15 @@ static NTSTATUS pvfs_acl_inherit_aces(struct pvfs_state *pvfs,
        for (i=0;i<parent_sd->dacl->num_aces;i++) {
                struct security_ace ace = parent_sd->dacl->aces[i];
                NTSTATUS status;
+               const struct dom_sid *creator = NULL, *new_id = NULL;
+               uint32_t orig_flags;
 
                if (!pvfs_inheritable_ace(pvfs, &ace, container)) {
                        continue;
                }
 
+               orig_flags = ace.flags;
+
                /* see the RAW-ACLS inheritance test for details on these rules */
                if (!container) {
                        ace.flags = 0;
@@ -453,7 +529,39 @@ static NTSTATUS pvfs_acl_inherit_aces(struct pvfs_state *pvfs,
                        }
                }
 
-               status = security_descriptor_dacl_add(sd, &ace);
+               /* the CREATOR sids are special when inherited */
+               if (dom_sid_equal(&ace.trustee, pvfs->sid_cache.creator_owner)) {
+                       creator = pvfs->sid_cache.creator_owner;
+                       new_id = sd->owner_sid;
+               } else if (dom_sid_equal(&ace.trustee, pvfs->sid_cache.creator_group)) {
+                       creator = pvfs->sid_cache.creator_group;
+                       new_id = sd->group_sid;
+               } else {
+                       new_id = &ace.trustee;
+               }
+
+               if (creator && container && 
+                   (ace.flags & SEC_ACE_FLAG_CONTAINER_INHERIT)) {
+                       uint32_t flags = ace.flags;
+
+                       ace.trustee = *new_id;
+                       ace.flags = 0;
+                       status = security_descriptor_dacl_add(sd, &ace);
+                       if (!NT_STATUS_IS_OK(status)) {
+                               return status;
+                       }
+
+                       ace.trustee = *creator;
+                       ace.flags = flags | SEC_ACE_FLAG_INHERIT_ONLY;
+                       status = security_descriptor_dacl_add(sd, &ace);
+               } else if (container && 
+                          !(orig_flags & SEC_ACE_FLAG_NO_PROPAGATE_INHERIT)) {
+                       status = security_descriptor_dacl_add(sd, &ace);
+               } else {
+                       ace.trustee = *new_id;
+                       status = security_descriptor_dacl_add(sd, &ace);
+               }
+
                if (!NT_STATUS_IS_OK(status)) {
                        return status;
                }