r4464: added pvfs backend support for the special CREATOR_OWNER and CREATOR_GROUP...
[samba.git] / source4 / ntvfs / posix / pvfs_acl.c
index 6eb4c13804f2a352f0af8193deb559828e0bd569..ba5fa96b07604058480754751870320513fce14d 100644 (file)
@@ -68,16 +68,8 @@ static NTSTATUS pvfs_default_acl(struct pvfs_state *pvfs,
                                 struct xattr_NTACL *acl)
 {
        struct security_descriptor *sd;
-       int i;
-       struct security_ace ace;
        NTSTATUS status;
-       const char *sid_names[] = {
-               SID_BUILTIN_ADMINISTRATORS,
-               SID_CREATOR_OWNER,
-               SID_CREATOR_GROUP,
-               SID_WORLD
-       };
-       uint32_t access_masks[4];
+       struct security_ace ace;
        mode_t mode;
 
        sd = security_descriptor_initialise(req);
@@ -96,91 +88,69 @@ static NTSTATUS pvfs_default_acl(struct pvfs_state *pvfs,
 
        sd->type |= SEC_DESC_DACL_PRESENT;
 
+       mode = name->st.st_mode;
+
        /*
-         we provide 4 ACEs
-           - Administrator
+         we provide up to 4 ACEs
            - Owner
            - Group
            - Everyone
+           - Administrator
         */
-       access_masks[0] = SEC_RIGHTS_FILE_ALL;
-       access_masks[1] = 0;
-       access_masks[2] = 0;
-       access_masks[3] = 0;
 
-       mode = name->st.st_mode;
+
+       /* setup owner ACE */
+       ace.type = SEC_ACE_TYPE_ACCESS_ALLOWED;
+       ace.flags = 0;
+       ace.trustee = *sd->owner_sid;
+       ace.access_mask = 0;
 
        if (mode & S_IRUSR) {
-               access_masks[1] |= 
-                       SEC_FILE_READ_DATA | 
-                       SEC_FILE_READ_EA |
-                       SEC_FILE_READ_ATTRIBUTE |
-                       SEC_FILE_EXECUTE |
-                       SEC_STD_SYNCHRONIZE |
-                       SEC_STD_READ_CONTROL;
+               if (mode & S_IWUSR) {
+                       ace.access_mask |= SEC_RIGHTS_FILE_ALL;
+               } else {
+                       ace.access_mask |= SEC_RIGHTS_FILE_READ | SEC_FILE_EXECUTE;
+               }
        }
        if (mode & S_IWUSR) {
-               access_masks[1] |= 
-                       SEC_FILE_WRITE_DATA | 
-                       SEC_FILE_APPEND_DATA |
-                       SEC_FILE_WRITE_EA |
-                       SEC_FILE_WRITE_ATTRIBUTE |
-                       SEC_STD_DELETE;
+               ace.access_mask |= SEC_RIGHTS_FILE_WRITE | SEC_STD_DELETE;
+       }
+       if (ace.access_mask) {
+               security_descriptor_dacl_add(sd, &ace);
        }
 
+
+       /* setup group ACE */
+       ace.trustee = *sd->group_sid;
+       ace.access_mask = 0;
        if (mode & S_IRGRP) {
-               access_masks[2] |= 
-                       SEC_FILE_READ_DATA | 
-                       SEC_FILE_READ_EA |
-                       SEC_FILE_READ_ATTRIBUTE |
-                       SEC_FILE_EXECUTE |
-                       SEC_STD_SYNCHRONIZE |
-                       SEC_STD_READ_CONTROL;
+               ace.access_mask |= SEC_RIGHTS_FILE_READ | SEC_FILE_EXECUTE;
        }
        if (mode & S_IWGRP) {
-               access_masks[2] |= 
-                       SEC_FILE_WRITE_DATA | 
-                       SEC_FILE_APPEND_DATA |
-                       SEC_FILE_WRITE_EA |
-                       SEC_FILE_WRITE_ATTRIBUTE;
+               /* note that delete is not granted - this matches posix behaviour */
+               ace.access_mask |= SEC_RIGHTS_FILE_WRITE;
+       }
+       if (ace.access_mask) {
+               security_descriptor_dacl_add(sd, &ace);
        }
 
+       /* setup other ACE */
+       ace.trustee = *dom_sid_parse_talloc(req, SID_WORLD);
+       ace.access_mask = 0;
        if (mode & S_IROTH) {
-               access_masks[3] |= 
-                       SEC_FILE_READ_DATA | 
-                       SEC_FILE_READ_EA |
-                       SEC_FILE_READ_ATTRIBUTE |
-                       SEC_FILE_EXECUTE |
-                       SEC_STD_SYNCHRONIZE |
-                       SEC_STD_READ_CONTROL;
+               ace.access_mask |= SEC_RIGHTS_FILE_READ | SEC_FILE_EXECUTE;
        }
        if (mode & S_IWOTH) {
-               access_masks[3] |= 
-                       SEC_FILE_WRITE_DATA | 
-                       SEC_FILE_APPEND_DATA |
-                       SEC_FILE_WRITE_EA |
-                       SEC_FILE_WRITE_ATTRIBUTE;
+               ace.access_mask |= SEC_RIGHTS_FILE_WRITE;
        }
-
-       ace.type = SEC_ACE_TYPE_ACCESS_ALLOWED;
-       ace.flags = 0;
-
-       for (i=0;i<ARRAY_SIZE(sid_names);i++) {
-               struct dom_sid *sid;
-
-               ace.access_mask = access_masks[i];
-
-               sid = dom_sid_parse_talloc(sd, sid_names[i]);
-               if (sid == NULL) {
-                       return NT_STATUS_NO_MEMORY;
-               }
-               ace.trustee = *sid;
-
-               status = security_descriptor_dacl_add(sd, &ace);
-               if (!NT_STATUS_IS_OK(status)) {
-                       return status;
-               }
+       if (ace.access_mask) {
+               security_descriptor_dacl_add(sd, &ace);
        }
+
+       /* setup system ACE */
+       ace.trustee = *dom_sid_parse_talloc(req, SID_NT_SYSTEM);
+       ace.access_mask = SEC_RIGHTS_FILE_ALL;
+       security_descriptor_dacl_add(sd, &ace);
        
        acl->version = 1;
        acl->info.sd = sd;
@@ -215,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;
        NTSTATUS status;
+       uid_t uid = -1;
+       gid_t gid = -1;
 
        acl = talloc_p(req, struct xattr_NTACL);
        if (acl == NULL) {
@@ -245,12 +218,28 @@ NTSTATUS pvfs_acl_set(struct pvfs_state *pvfs,
 
        new_sd = info->set_secdesc.in.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;
@@ -258,9 +247,24 @@ 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);
        }
 
+       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);
+               }
+       }
+
        status = pvfs_acl_save(pvfs, name, fd, acl);
 
        return status;
@@ -336,6 +340,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,7 +356,7 @@ NTSTATUS pvfs_access_check(struct pvfs_state *pvfs,
                           struct pvfs_filename *name,
                           uint32_t *access_mask)
 {
-       struct nt_user_token *token = req->session->session_info->nt_user_token;
+       struct security_token *token = req->session->session_info->security_token;
        struct xattr_NTACL *acl;
        NTSTATUS status;
        struct security_descriptor *sd;
@@ -390,3 +396,230 @@ NTSTATUS pvfs_access_check(struct pvfs_state *pvfs,
        
        return status;
 }
+
+
+/*
+  a simplified interface to access check, designed for calls that
+  do not take or return an access check mask
+*/
+NTSTATUS pvfs_access_check_simple(struct pvfs_state *pvfs, 
+                                 struct smbsrv_request *req,
+                                 struct pvfs_filename *name,
+                                 uint32_t access_needed)
+{
+       if (access_needed == 0) {
+               return NT_STATUS_OK;
+       }
+       return pvfs_access_check(pvfs, req, name, &access_needed);
+}
+
+/*
+  access check for creating a new file/directory
+*/
+NTSTATUS pvfs_access_check_create(struct pvfs_state *pvfs, 
+                                 struct smbsrv_request *req,
+                                 struct pvfs_filename *name)
+{
+       struct pvfs_filename *parent;
+       NTSTATUS status;
+
+       status = pvfs_resolve_parent(pvfs, req, name, &parent);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       return pvfs_access_check_simple(pvfs, req, parent, SEC_DIR_ADD_FILE);
+}
+
+
+/*
+  determine if an ACE is inheritable
+*/
+static BOOL pvfs_inheritable_ace(struct pvfs_state *pvfs,
+                                const struct security_ace *ace,
+                                BOOL container)
+{
+       if (!container) {
+               return (ace->flags & SEC_ACE_FLAG_OBJECT_INHERIT) != 0;
+       }
+
+       if (ace->flags & SEC_ACE_FLAG_CONTAINER_INHERIT) {
+               return True;
+       }
+
+       if ((ace->flags & SEC_ACE_FLAG_OBJECT_INHERIT) &&
+           !(ace->flags & SEC_ACE_FLAG_NO_PROPAGATE_INHERIT)) {
+               return True;
+       }
+
+       return False;
+}
+
+/*
+  this is the core of ACL inheritance. It copies any inheritable
+  aces from the parent SD to the child SD. Note that the algorithm 
+  depends on whether the child is a container or not
+*/
+static NTSTATUS pvfs_acl_inherit_aces(struct pvfs_state *pvfs, 
+                                     struct security_descriptor *parent_sd,
+                                     struct security_descriptor *sd,
+                                     BOOL container)
+{
+       int i;
+       
+       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;
+               } else {
+                       ace.flags &= ~SEC_ACE_FLAG_INHERIT_ONLY;
+
+                       if (!(ace.flags & SEC_ACE_FLAG_CONTAINER_INHERIT)) {
+                               ace.flags |= SEC_ACE_FLAG_INHERIT_ONLY;
+                       }
+                       if (ace.flags & SEC_ACE_FLAG_NO_PROPAGATE_INHERIT) {
+                               ace.flags = 0;
+                       }
+               }
+
+               /* 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;
+               }
+       }
+
+       return NT_STATUS_OK;
+}
+
+
+
+/*
+  setup an ACL on a new file/directory based on the inherited ACL from
+  the parent. If there is no inherited ACL then we don't set anything,
+  as the default ACL applies anyway
+*/
+NTSTATUS pvfs_acl_inherit(struct pvfs_state *pvfs, 
+                         struct smbsrv_request *req,
+                         struct pvfs_filename *name,
+                         int fd)
+{
+       struct xattr_NTACL *acl;
+       NTSTATUS status;
+       struct pvfs_filename *parent;
+       struct security_descriptor *parent_sd, *sd;
+       BOOL container;
+
+       /* form the parents path */
+       status = pvfs_resolve_parent(pvfs, req, name, &parent);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       acl = talloc_p(req, struct xattr_NTACL);
+       if (acl == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       status = pvfs_acl_load(pvfs, parent, -1, acl);
+       if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
+               return NT_STATUS_OK;
+       }
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       switch (acl->version) {
+       case 1:
+               parent_sd = acl->info.sd;
+               break;
+       default:
+               return NT_STATUS_INVALID_ACL;
+       }
+
+       if (parent_sd == NULL ||
+           parent_sd->dacl == NULL ||
+           parent_sd->dacl->num_aces == 0) {
+               /* go with the default ACL */
+               return NT_STATUS_OK;
+       }
+
+       /* create the new sd */
+       sd = security_descriptor_initialise(req);
+       if (sd == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       status = sidmap_uid_to_sid(pvfs->sidmap, sd, name->st.st_uid, &sd->owner_sid);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+       status = sidmap_gid_to_sid(pvfs->sidmap, sd, name->st.st_gid, &sd->group_sid);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       sd->type |= SEC_DESC_DACL_PRESENT;
+
+       container = (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) ? True:False;
+
+       /* fill in the aces from the parent */
+       status = pvfs_acl_inherit_aces(pvfs, parent_sd, sd, container);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       /* if there is nothing to inherit then we fallback to the
+          default acl */
+       if (sd->dacl == NULL || sd->dacl->num_aces == 0) {
+               return NT_STATUS_OK;
+       }
+
+       acl->info.sd = sd;
+
+       status = pvfs_acl_save(pvfs, name, fd, acl);
+       
+       return status;
+}