r4403: - added ACL inheritance in the pvfs backend. ACLs are now inherited on
authorAndrew Tridgell <tridge@samba.org>
Thu, 30 Dec 2004 02:25:20 +0000 (02:25 +0000)
committerGerald (Jerry) Carter <jerry@samba.org>
Wed, 10 Oct 2007 18:07:43 +0000 (13:07 -0500)
  file and directory creation via ntcreatex. pvfs now passes the
  inheritance test in RAW-ACLS

- cleaned up the error handling a bit in pvfs_open()
(This used to be commit f4dfb63d5395a365961a21388639809fcd3112d0)

source4/ntvfs/posix/pvfs_acl.c
source4/ntvfs/posix/pvfs_open.c
source4/ntvfs/posix/pvfs_resolve.c

index 970cddf6f7ddb9ff0edb4f986557eeb359a2893e..c2309b92ea81ce975985510dbcfc7981757a3840 100644 (file)
@@ -106,7 +106,11 @@ static NTSTATUS pvfs_default_acl(struct pvfs_state *pvfs,
        ace.access_mask = 0;
 
        if (mode & S_IRUSR) {
-               ace.access_mask |= SEC_RIGHTS_FILE_READ | SEC_FILE_EXECUTE;
+               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) {
                ace.access_mask |= SEC_RIGHTS_FILE_WRITE | SEC_STD_DELETE;
@@ -123,6 +127,7 @@ static NTSTATUS pvfs_default_acl(struct pvfs_state *pvfs,
                ace.access_mask |= SEC_RIGHTS_FILE_READ | SEC_FILE_EXECUTE;
        }
        if (mode & S_IWGRP) {
+               /* note that delete is not granted - this matches posix behaviour */
                ace.access_mask |= SEC_RIGHTS_FILE_WRITE;
        }
        if (ace.access_mask) {
@@ -368,3 +373,160 @@ NTSTATUS pvfs_access_check_simple(struct pvfs_state *pvfs,
 {
        return pvfs_access_check(pvfs, req, name, &access_needed);
 }
+
+
+/*
+  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;
+
+               if (!pvfs_inheritable_ace(pvfs, &ace, container)) {
+                       continue;
+               }
+
+               /* 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;
+                       }
+               }
+
+               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;
+}
index 34052fc44aefe2a5a49f496a63944fd48aa6b818..1695d8e1d9bb50dd703e13d492922cfc953c9081 100644 (file)
@@ -95,6 +95,43 @@ static int pvfs_dir_fnum_destructor(void *p)
        return 0;
 }
 
+/*
+  setup any EAs and the ACL on newly created files/directories
+*/
+static NTSTATUS pvfs_open_setup_eas_acl(struct pvfs_state *pvfs,
+                                       struct smbsrv_request *req,
+                                       struct pvfs_filename *name,
+                                       int fd, int fnum,
+                                       union smb_open *io)
+{
+       NTSTATUS status;
+
+       /* setup any EAs that were asked for */
+       if (io->ntcreatex.in.ea_list) {
+               status = pvfs_setfileinfo_ea_set(pvfs, name, fd, 
+                                                io->ntcreatex.in.ea_list->num_eas,
+                                                io->ntcreatex.in.ea_list->eas);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return status;
+               }
+       }
+
+       /* setup an initial sec_desc if requested */
+       if (io->ntcreatex.in.sec_desc) {
+               union smb_setfileinfo set;
+
+               set.set_secdesc.file.fnum = fnum;
+               set.set_secdesc.in.secinfo_flags = SECINFO_DACL;
+               set.set_secdesc.in.sd = io->ntcreatex.in.sec_desc;
+
+               status = pvfs_acl_set(pvfs, req, name, fd, &set);
+       } else {
+               /* otherwise setup an inherited acl from the parent */
+               status = pvfs_acl_inherit(pvfs, req, name, fd);
+       }
+
+       return status;
+}
 
 /*
   open a directory
@@ -162,6 +199,7 @@ static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs,
                /* check the security descriptor */
                status = pvfs_access_check(pvfs, req, name, &access_mask);
                if (!NT_STATUS_IS_OK(status)) {
+                       idr_remove(pvfs->idtree_fnum, fnum);
                        return status;
                }
        }
@@ -200,6 +238,7 @@ static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs,
                uint32_t attrib = io->generic.in.file_attr | FILE_ATTRIBUTE_DIRECTORY;
                mode_t mode = pvfs_fileperms(pvfs, attrib);
                if (mkdir(name->full_name, mode) == -1) {
+                       idr_remove(pvfs->idtree_fnum, fnum);
                        return pvfs_map_errno(pvfs,errno);
                }
 
@@ -207,14 +246,21 @@ static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs,
 
                status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname, 0, &name);
                if (!NT_STATUS_IS_OK(status)) {
-                       return status;
+                       goto cleanup_delete;
                }
+
+               status = pvfs_open_setup_eas_acl(pvfs, req, name, -1, fnum, io);
+               if (!NT_STATUS_IS_OK(status)) {
+                       goto cleanup_delete;
+               }
+
                create_action = NTCREATEX_ACTION_CREATED;
        } else {
                create_action = NTCREATEX_ACTION_EXISTED;
        }
 
        if (!name->exists) {
+               idr_remove(pvfs->idtree_fnum, fnum);
                return NT_STATUS_OBJECT_NAME_NOT_FOUND;
        }
 
@@ -236,6 +282,11 @@ static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs,
        io->generic.out.is_directory  = 1;
 
        return NT_STATUS_OK;
+
+cleanup_delete:
+       idr_remove(pvfs->idtree_fnum, fnum);
+       rmdir(name->full_name);
+       return status;
 }
 
 /*
@@ -462,53 +513,25 @@ static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs,
        name->dos.attrib = attrib;
        status = pvfs_dosattrib_save(pvfs, name, fd);
        if (!NT_STATUS_IS_OK(status)) {
-               idr_remove(pvfs->idtree_fnum, fnum);
-               close(fd);
-               return status;
+               goto cleanup_delete;
        }
 
-       /* setup any EAs that were asked for */
-       if (io->ntcreatex.in.ea_list) {
-               status = pvfs_setfileinfo_ea_set(pvfs, name, fd, 
-                                                io->ntcreatex.in.ea_list->num_eas,
-                                                io->ntcreatex.in.ea_list->eas);
-               if (!NT_STATUS_IS_OK(status)) {
-                       idr_remove(pvfs->idtree_fnum, fnum);
-                       close(fd);
-                       return status;
-               }
-       }
-
-       /* setup an initial sec_desc is required */
-       if (io->ntcreatex.in.sec_desc) {
-               union smb_setfileinfo set;
-
-               set.set_secdesc.file.fnum = fnum;
-               set.set_secdesc.in.secinfo_flags = SECINFO_DACL;
-               set.set_secdesc.in.sd = io->ntcreatex.in.sec_desc;
 
-               status = pvfs_acl_set(pvfs, req, name, fd, &set);
-               if (!NT_STATUS_IS_OK(status)) {
-                       idr_remove(pvfs->idtree_fnum, fnum);
-                       close(fd);
-                       return status;
-               }
+       status = pvfs_open_setup_eas_acl(pvfs, req, name, fd, fnum, io);
+       if (!NT_STATUS_IS_OK(status)) {
+               goto cleanup_delete;
        }
 
        /* form the lock context used for byte range locking and
           opendb locking */
        status = pvfs_locking_key(name, f->handle, &f->handle->odb_locking_key);
        if (!NT_STATUS_IS_OK(status)) {
-               idr_remove(pvfs->idtree_fnum, fnum);
-               close(fd);
-               return status;
+               goto cleanup_delete;
        }
 
        status = pvfs_brl_locking_key(name, f->handle, &f->handle->brl_locking_key);
        if (!NT_STATUS_IS_OK(status)) {
-               idr_remove(pvfs->idtree_fnum, fnum);
-               close(fd);
-               return status;
+               goto cleanup_delete;
        }
 
        /* grab a lock on the open file record */
@@ -518,16 +541,17 @@ static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs,
                         name->full_name));
                /* we were supposed to do a blocking lock, so something
                   is badly wrong! */
-               idr_remove(pvfs->idtree_fnum, fnum);
-               close(fd);
-               return NT_STATUS_INTERNAL_DB_CORRUPTION;
+               status = NT_STATUS_INTERNAL_DB_CORRUPTION;
+               goto cleanup_delete;
        }
 
        status = odb_open_file(lck, f->handle, name->stream_id,
                               share_access, create_options, access_mask);
        talloc_free(lck);
        if (!NT_STATUS_IS_OK(status)) {
-               /* bad news, we must have hit a race */
+               /* bad news, we must have hit a race - we don't delete the file
+                  here as the most likely scenario is that someone else created 
+                  the file at the same time */
                idr_remove(pvfs->idtree_fnum, fnum);
                close(fd);
                return status;
@@ -583,6 +607,12 @@ static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs,
        talloc_steal(pvfs, f);
 
        return NT_STATUS_OK;
+
+cleanup_delete:
+       idr_remove(pvfs->idtree_fnum, fnum);
+       close(fd);
+       unlink(name->full_name);
+       return status;
 }
 
 
@@ -846,6 +876,10 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
                return pvfs_open_directory(pvfs, req, name, io);
        }
 
+       /* FILE_ATTRIBUTE_DIRECTORY is ignored if the above test for directory
+          open doesn't match */
+       io->generic.in.file_attr &= ~FILE_ATTRIBUTE_DIRECTORY;
+
        create_options = io->generic.in.create_options;
        share_access   = io->generic.in.share_access;
        access_mask    = io->generic.in.access_mask;
@@ -894,10 +928,6 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
                return NT_STATUS_INVALID_PARAMETER;
        }
 
-       if (io->generic.in.file_attr & FILE_ATTRIBUTE_DIRECTORY) {
-               return NT_STATUS_INVALID_PARAMETER;
-       }
-
        /* handle creating a new file separately */
        if (!name->exists) {
                status = pvfs_create_file(pvfs, req, name, io);
index 7e7f49d0afdb50961b0addf98a2ac95d78f6f676..4ad34767950577aff649cbc3d625d1e5310e895f 100644 (file)
@@ -579,3 +579,54 @@ NTSTATUS pvfs_resolve_name_fd(struct pvfs_state *pvfs, int fd,
        
        return pvfs_fill_dos_info(pvfs, name, fd);
 }
+
+
+/*
+  resolve the parent of a given name
+*/
+NTSTATUS pvfs_resolve_parent(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx,
+                            const struct pvfs_filename *child,
+                            struct pvfs_filename **name)
+{
+       NTSTATUS status;
+       char *p;
+
+       *name = talloc_p(mem_ctx, struct pvfs_filename);
+       if (*name == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       (*name)->full_name = talloc_strdup(*name, child->full_name);
+       if ((*name)->full_name == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       p = strrchr_m((*name)->full_name, '/');
+       if (p == NULL) {
+               return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
+       }
+
+       /* this handles the root directory */
+       if (p == (*name)->full_name) {
+               p[1] = 0;
+       } else {
+               p[0] = 0;
+       }
+
+       if (stat((*name)->full_name, &(*name)->st) == -1) {
+               return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+       }
+
+       (*name)->exists = True;
+       (*name)->stream_exists = True;
+       (*name)->has_wildcard = False;
+       /* we can't get the correct 'original_name', but for the purposes
+          of this call this is close enough */
+       (*name)->original_name = talloc_reference(*name, child->original_name);
+       (*name)->stream_name = NULL;
+       (*name)->stream_id = 0;
+
+       status = pvfs_fill_dos_info(pvfs, *name, -1);
+
+       return status;
+}