Author: mav
Date: Wed May 11 12:58:12 2016
New Revision: 299442
URL: https://svnweb.freebsd.org/changeset/base/299442

Log:
  6762 POSIX write should imply DELETE_CHILD on directories - and
  some additional considerations
  
  Reviewed by: Gordon Ross <g...@nexenta.com>
  Reviewed by: Yuri Pankov <yuri.pan...@nexenta.com>
  Author: Kevin Crowe <kevin.cr...@nexenta.com>
  
  openzfs/openzfs@d316fffc9c361532a482208561bbb614dac7f916

Modified:
  vendor-sys/illumos/dist/common/acl/acl_common.c
  vendor-sys/illumos/dist/uts/common/fs/zfs/zfs_acl.c
  vendor-sys/illumos/dist/uts/common/sys/acl.h

Modified: vendor-sys/illumos/dist/common/acl/acl_common.c
==============================================================================
--- vendor-sys/illumos/dist/common/acl/acl_common.c     Wed May 11 12:54:00 
2016        (r299441)
+++ vendor-sys/illumos/dist/common/acl/acl_common.c     Wed May 11 12:58:12 
2016        (r299442)
@@ -20,7 +20,7 @@
  */
 /*
  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2011 Nexenta Systems, Inc.  All rights reserved.
+ * Copyright 2014 Nexenta Systems, Inc.  All rights reserved.
  */
 
 #include <sys/types.h>
@@ -1575,7 +1575,8 @@ acl_trivial_access_masks(mode_t mode, bo
        uint32_t write_mask = ACE_WRITE_DATA|ACE_APPEND_DATA;
        uint32_t execute_mask = ACE_EXECUTE;
 
-       (void) isdir;   /* will need this later */
+       if (isdir)
+               write_mask |= ACE_DELETE_CHILD;
 
        masks->deny1 = 0;
        if (!(mode & S_IRUSR) && (mode & (S_IRGRP|S_IROTH)))
@@ -1719,10 +1720,17 @@ ace_trivial_common(void *acep, int aclcn
                        return (1);
 
                /*
-                * Delete permissions are never set by default
+                * Delete permission is never set by default
+                */
+               if (mask & ACE_DELETE)
+                       return (1);
+
+               /*
+                * Child delete permission should be accompanied by write
                 */
-               if (mask & (ACE_DELETE|ACE_DELETE_CHILD))
+               if ((mask & ACE_DELETE_CHILD) && !(mask & ACE_WRITE_DATA))
                        return (1);
+
                /*
                 * only allow owner@ to have
                 * write_acl/write_owner/write_attributes/write_xattr/

Modified: vendor-sys/illumos/dist/uts/common/fs/zfs/zfs_acl.c
==============================================================================
--- vendor-sys/illumos/dist/uts/common/fs/zfs/zfs_acl.c Wed May 11 12:54:00 
2016        (r299441)
+++ vendor-sys/illumos/dist/uts/common/fs/zfs/zfs_acl.c Wed May 11 12:58:12 
2016        (r299442)
@@ -20,8 +20,8 @@
  */
 /*
  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2011 Nexenta Systems, Inc.  All rights reserved.
  * Copyright (c) 2013 by Delphix. All rights reserved.
+ * Copyright 2014 Nexenta Systems, Inc.  All rights reserved.
  */
 
 #include <sys/types.h>
@@ -2080,7 +2080,7 @@ zfs_zaccess_dataset_check(znode_t *zp, u
  * placed into the working_mode, giving the caller a mask of denied
  * accesses.  Returns:
  *     0               if all AoI granted
- *     EACCESS         if the denied mask is non-zero
+ *     EACCES          if the denied mask is non-zero
  *     other error     if abnormal failure (e.g., IO error)
  *
  * A secondary usage of the function is to determine if any of the
@@ -2517,46 +2517,30 @@ zfs_zaccess_unix(znode_t *zp, mode_t mod
        return (zfs_zaccess(zp, v4_mode, 0, B_FALSE, cr));
 }
 
-static int
-zfs_delete_final_check(znode_t *zp, znode_t *dzp,
-    mode_t available_perms, cred_t *cr)
-{
-       int error;
-       uid_t downer;
-
-       downer = zfs_fuid_map_id(dzp->z_zfsvfs, dzp->z_uid, cr, ZFS_OWNER);
-
-       error = secpolicy_vnode_access2(cr, ZTOV(dzp),
-           downer, available_perms, VWRITE|VEXEC);
-
-       if (error == 0)
-               error = zfs_sticky_remove_access(dzp, zp, cr);
-
-       return (error);
-}
+/* See zfs_zaccess_delete() */
+int zfs_write_implies_delete_child = 1;
 
 /*
- * Determine whether Access should be granted/deny, without
- * consulting least priv subsystem.
+ * Determine whether delete access should be granted.
  *
  * The following chart is the recommended NFSv4 enforcement for
  * ability to delete an object.
  *
  *      -------------------------------------------------------
- *      |   Parent Dir  |           Target Object Permissions |
+ *      |   Parent Dir  |      Target Object Permissions      |
  *      |  permissions  |                                     |
  *      -------------------------------------------------------
  *      |               | ACL Allows | ACL Denies| Delete     |
  *      |               |  Delete    |  Delete   | unspecified|
  *      -------------------------------------------------------
- *      |  ACL Allows   | Permit     | Permit    | Permit     |
- *      |  DELETE_CHILD |                                     |
+ *      |  ACL Allows   | Permit     | Permit *  | Permit     |
+ *      |  DELETE_CHILD |            |           |            |
  *      -------------------------------------------------------
- *      |  ACL Denies   | Permit     | Deny      | Deny       |
+ *      |  ACL Denies   | Permit *   | Deny      | Deny       |
  *      |  DELETE_CHILD |            |           |            |
  *      -------------------------------------------------------
  *      | ACL specifies |            |           |            |
- *      | only allow    | Permit     | Permit    | Permit     |
+ *      | only allow    | Permit     | Permit *  | Permit     |
  *      | write and     |            |           |            |
  *      | execute       |            |           |            |
  *      -------------------------------------------------------
@@ -2566,91 +2550,174 @@ zfs_delete_final_check(znode_t *zp, znod
  *      -------------------------------------------------------
  *         ^
  *         |
- *         No search privilege, can't even look up file?
+ *         Re. execute permission on the directory:  if that's missing,
+ *        the vnode lookup of the target will fail before we get here.
+ *
+ * Re [*] in the table above:  We are intentionally disregarding the
+ * NFSv4 committee recommendation for these three cells of the matrix
+ * because that recommendation conflicts with the behavior expected
+ * by Windows clients for ACL evaluation.  See acl.h for notes on
+ * which ACE_... flags should be checked for which operations.
+ * Specifically, the NFSv4 committee recommendation is in conflict
+ * with the Windows interpretation of DENY ACEs, where DENY ACEs
+ * should take precedence ahead of ALLOW ACEs.
+ *
+ * This implementation takes a conservative approach by checking for
+ * DENY ACEs on both the target object and it's container; checking
+ * the ACE_DELETE on the target object, and ACE_DELETE_CHILD on the
+ * container.  If a DENY ACE is found for either of those, delete
+ * access is denied.  (Note that DENY ACEs are very rare.)
+ *
+ * Note that after these changes, entire the second row and the
+ * entire middle column of the table above change to Deny.
+ * Accordingly, the logic here is somewhat simplified.
  *
+ * First check for DENY ACEs that apply.
+ * If either target or container has a deny, EACCES.
+ *
+ * Delete access can then be summarized as follows:
+ * 1: The object to be deleted grants ACE_DELETE, or
+ * 2: The containing directory grants ACE_DELETE_CHILD.
+ * In a Windows system, that would be the end of the story.
+ * In this system, (2) has some complications...
+ * 2a: "sticky" bit on a directory adds restrictions, and
+ * 2b: existing ACEs from previous versions of ZFS may
+ * not carry ACE_DELETE_CHILD where they should, so we
+ * also allow delete when ACE_WRITE_DATA is granted.
+ *
+ * Note: 2b is technically a work-around for a prior bug,
+ * which hopefully can go away some day.  For those who
+ * no longer need the work around, and for testing, this
+ * work-around is made conditional via the tunable:
+ * zfs_write_implies_delete_child
  */
 int
 zfs_zaccess_delete(znode_t *dzp, znode_t *zp, cred_t *cr)
 {
+       uint32_t wanted_dirperms;
        uint32_t dzp_working_mode = 0;
        uint32_t zp_working_mode = 0;
        int dzp_error, zp_error;
-       mode_t available_perms;
-       boolean_t dzpcheck_privs = B_TRUE;
-       boolean_t zpcheck_privs = B_TRUE;
-
-       /*
-        * We want specific DELETE permissions to
-        * take precedence over WRITE/EXECUTE.  We don't
-        * want an ACL such as this to mess us up.
-        * user:joe:write_data:deny,user:joe:delete:allow
-        *
-        * However, deny permissions may ultimately be overridden
-        * by secpolicy_vnode_access().
-        *
-        * We will ask for all of the necessary permissions and then
-        * look at the working modes from the directory and target object
-        * to determine what was found.
-        */
+       boolean_t dzpcheck_privs;
+       boolean_t zpcheck_privs;
 
        if (zp->z_pflags & (ZFS_IMMUTABLE | ZFS_NOUNLINK))
                return (SET_ERROR(EPERM));
 
        /*
-        * First row
-        * If the directory permissions allow the delete, we are done.
-        */
-       if ((dzp_error = zfs_zaccess_common(dzp, ACE_DELETE_CHILD,
-           &dzp_working_mode, &dzpcheck_privs, B_FALSE, cr)) == 0)
-               return (0);
+        * Case 1:
+        * If target object grants ACE_DELETE then we are done.  This is
+        * indicated by a return value of 0.  For this case we don't worry
+        * about the sticky bit because sticky only applies to the parent
+        * directory and this is the child access result.
+        *
+        * If we encounter a DENY ACE here, we're also done (EACCES).
+        * Note that if we hit a DENY ACE here (on the target) it should
+        * take precedence over a DENY ACE on the container, so that when
+        * we have more complete auditing support we will be able to
+        * report an access failure against the specific target.
+        * (This is part of why we're checking the target first.)
+        */
+       zp_error = zfs_zaccess_common(zp, ACE_DELETE, &zp_working_mode,
+           &zpcheck_privs, B_FALSE, cr);
+       if (zp_error == EACCES) {
+               /* We hit a DENY ACE. */
+               if (!zpcheck_privs)
+                       return (SET_ERROR(zp_error));
+               return (secpolicy_vnode_remove(cr));
 
-       /*
-        * If target object has delete permission then we are done
-        */
-       if ((zp_error = zfs_zaccess_common(zp, ACE_DELETE, &zp_working_mode,
-           &zpcheck_privs, B_FALSE, cr)) == 0)
+       }
+       if (zp_error == 0)
                return (0);
 
-       ASSERT(dzp_error && zp_error);
+       /*
+        * Case 2:
+        * If the containing directory grants ACE_DELETE_CHILD,
+        * or we're in backward compatibility mode and the
+        * containing directory has ACE_WRITE_DATA, allow.
+        * Case 2b is handled with wanted_dirperms.
+        */
+       wanted_dirperms = ACE_DELETE_CHILD;
+       if (zfs_write_implies_delete_child)
+               wanted_dirperms |= ACE_WRITE_DATA;
+       dzp_error = zfs_zaccess_common(dzp, wanted_dirperms,
+           &dzp_working_mode, &dzpcheck_privs, B_FALSE, cr);
+       if (dzp_error == EACCES) {
+               /* We hit a DENY ACE. */
+               if (!dzpcheck_privs)
+                       return (SET_ERROR(dzp_error));
+               return (secpolicy_vnode_remove(cr));
+       }
 
-       if (!dzpcheck_privs)
-               return (dzp_error);
-       if (!zpcheck_privs)
-               return (zp_error);
+       /*
+        * Cases 2a, 2b (continued)
+        *
+        * Note: dzp_working_mode now contains any permissions
+        * that were NOT granted.  Therefore, if any of the
+        * wanted_dirperms WERE granted, we will have:
+        *   dzp_working_mode != wanted_dirperms
+        * We're really asking if ANY of those permissions
+        * were granted, and if so, grant delete access.
+        */
+       if (dzp_working_mode != wanted_dirperms)
+               dzp_error = 0;
 
        /*
-        * Second row
+        * dzp_error is 0 if the container granted us permissions to "modify".
+        * If we do not have permission via one or more ACEs, our current
+        * privileges may still permit us to modify the container.
         *
-        * If directory returns EACCES then delete_child was denied
-        * due to deny delete_child.  In this case send the request through
-        * secpolicy_vnode_remove().  We don't use zfs_delete_final_check()
-        * since that *could* allow the delete based on write/execute permission
-        * and we want delete permissions to override write/execute.
+        * dzpcheck_privs is false when i.e. the FS is read-only.
+        * Otherwise, do privilege checks for the container.
         */
+       if (dzp_error != 0 && dzpcheck_privs) {
+               uid_t owner;
 
-       if (dzp_error == EACCES)
-               return (secpolicy_vnode_remove(cr));
+               /*
+                * The secpolicy call needs the requested access and
+                * the current access mode of the container, but it
+                * only knows about Unix-style modes (VEXEC, VWRITE),
+                * so this must condense the fine-grained ACE bits into
+                * Unix modes.
+                *
+                * The VEXEC flag is easy, because we know that has
+                * always been checked before we get here (during the
+                * lookup of the target vnode).  The container has not
+                * granted us permissions to "modify", so we do not set
+                * the VWRITE flag in the current access mode.
+                */
+               owner = zfs_fuid_map_id(dzp->z_zfsvfs, dzp->z_uid, cr,
+                   ZFS_OWNER);
+               dzp_error = secpolicy_vnode_access2(cr, ZTOV(dzp),
+                   owner, VEXEC, VWRITE|VEXEC);
+       }
+       if (dzp_error != 0) {
+               /*
+                * Note: We may have dzp_error = -1 here (from
+                * zfs_zacess_common).  Don't return that.
+                */
+               return (SET_ERROR(EACCES));
+       }
 
        /*
-        * Third Row
-        * only need to see if we have write/execute on directory.
+        * At this point, we know that the directory permissions allow
+        * us to modify, but we still need to check for the additional
+        * restrictions that apply when the "sticky bit" is set.
+        *
+        * Yes, zfs_sticky_remove_access() also checks this bit, but
+        * checking it here and skipping the call below is nice when
+        * you're watching all of this with dtrace.
         */
-
-       dzp_error = zfs_zaccess_common(dzp, ACE_EXECUTE|ACE_WRITE_DATA,
-           &dzp_working_mode, &dzpcheck_privs, B_FALSE, cr);
-
-       if (dzp_error != 0 && !dzpcheck_privs)
-               return (dzp_error);
+       if ((dzp->z_mode & S_ISVTX) == 0)
+               return (0);
 
        /*
-        * Fourth row
+        * zfs_sticky_remove_access will succeed if:
+        * 1. The sticky bit is absent.
+        * 2. We pass the sticky bit restrictions.
+        * 3. We have privileges that always allow file removal.
         */
-
-       available_perms = (dzp_working_mode & ACE_WRITE_DATA) ? 0 : VWRITE;
-       available_perms |= (dzp_working_mode & ACE_EXECUTE) ? 0 : VEXEC;
-
-       return (zfs_delete_final_check(zp, dzp, available_perms, cr));
-
+       return (zfs_sticky_remove_access(dzp, zp, cr));
 }
 
 int

Modified: vendor-sys/illumos/dist/uts/common/sys/acl.h
==============================================================================
--- vendor-sys/illumos/dist/uts/common/sys/acl.h        Wed May 11 12:54:00 
2016        (r299441)
+++ vendor-sys/illumos/dist/uts/common/sys/acl.h        Wed May 11 12:58:12 
2016        (r299442)
@@ -23,6 +23,8 @@
  *
  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ *
+ * Copyright 2014 Nexenta Systems, Inc.  All rights reserved.
  */
 
 #ifndef _SYS_ACL_H
@@ -76,37 +78,55 @@ typedef struct acl_info acl_t;
 
 /*
  * The following are defined for ace_t.
+ *
+ * Note, these are intentionally the same as the Windows
+ * "File Access Rights Constants" you can find on MSDN.
+ * (See also: "Standard Access Rights" on MSDN).
+ *
+ * The equivalent Windows names for these are just like
+ * those show below, with FILE_ in place of ACE_, except
+ * as noted below.  Also note that Windows uses a special
+ * privilege: BYPASS_TRAVERSE_CHECKING, normally granted
+ * to everyone, that causes the absence of ACE_TRAVERSE
+ * to be ignored.
+ */
+#define        ACE_READ_DATA           0x00000001      /* file: read data */
+#define        ACE_LIST_DIRECTORY      0x00000001      /* dir: list files */
+#define        ACE_WRITE_DATA          0x00000002      /* file: write data */
+#define        ACE_ADD_FILE            0x00000002      /* dir: create file */
+#define        ACE_APPEND_DATA         0x00000004      /* file: append data */
+#define        ACE_ADD_SUBDIRECTORY    0x00000004      /* dir: create subdir */
+#define        ACE_READ_NAMED_ATTRS    0x00000008      /* FILE_READ_EA */
+#define        ACE_WRITE_NAMED_ATTRS   0x00000010      /* FILE_WRITE_EA */
+#define        ACE_EXECUTE             0x00000020      /* file: execute */
+#define        ACE_TRAVERSE            0x00000020      /* dir: lookup name */
+#define        ACE_DELETE_CHILD        0x00000040      /* dir: unlink child */
+#define        ACE_READ_ATTRIBUTES     0x00000080      /* (all) stat, etc. */
+#define        ACE_WRITE_ATTRIBUTES    0x00000100      /* (all) utimes, etc. */
+#define        ACE_DELETE              0x00010000      /* (all) unlink self */
+#define        ACE_READ_ACL            0x00020000      /* (all) getsecattr */
+#define        ACE_WRITE_ACL           0x00040000      /* (all) setsecattr */
+#define        ACE_WRITE_OWNER         0x00080000      /* (all) chown */
+#define        ACE_SYNCHRONIZE         0x00100000      /* (all) see MSDN */
+
+/*
+ * Some of the following are the same as Windows uses. (but NOT ALL!)
+ * See the "ACE_HEADER" structure description on MSDN for details.
+ * Comments show relations to the MSDN names.
  */
-#define        ACE_READ_DATA           0x00000001
-#define        ACE_LIST_DIRECTORY      0x00000001
-#define        ACE_WRITE_DATA          0x00000002
-#define        ACE_ADD_FILE            0x00000002
-#define        ACE_APPEND_DATA         0x00000004
-#define        ACE_ADD_SUBDIRECTORY    0x00000004
-#define        ACE_READ_NAMED_ATTRS    0x00000008
-#define        ACE_WRITE_NAMED_ATTRS   0x00000010
-#define        ACE_EXECUTE             0x00000020
-#define        ACE_DELETE_CHILD        0x00000040
-#define        ACE_READ_ATTRIBUTES     0x00000080
-#define        ACE_WRITE_ATTRIBUTES    0x00000100
-#define        ACE_DELETE              0x00010000
-#define        ACE_READ_ACL            0x00020000
-#define        ACE_WRITE_ACL           0x00040000
-#define        ACE_WRITE_OWNER         0x00080000
-#define        ACE_SYNCHRONIZE         0x00100000
-
-#define        ACE_FILE_INHERIT_ACE            0x0001
-#define        ACE_DIRECTORY_INHERIT_ACE       0x0002
-#define        ACE_NO_PROPAGATE_INHERIT_ACE    0x0004
-#define        ACE_INHERIT_ONLY_ACE            0x0008
+#define        ACE_FILE_INHERIT_ACE            0x0001  /* = OBJECT_INHERIT_ACE 
*/
+#define        ACE_DIRECTORY_INHERIT_ACE       0x0002  /* = 
CONTAINER_INHERIT_ACE */
+#define        ACE_NO_PROPAGATE_INHERIT_ACE    0x0004  /* = 
NO_PROPAGATE_INHERIT_ACE */
+#define        ACE_INHERIT_ONLY_ACE            0x0008  /* = INHERIT_ONLY_ACE */
 #define        ACE_SUCCESSFUL_ACCESS_ACE_FLAG  0x0010
 #define        ACE_FAILED_ACCESS_ACE_FLAG      0x0020
 #define        ACE_IDENTIFIER_GROUP            0x0040
-#define        ACE_INHERITED_ACE               0x0080
+#define        ACE_INHERITED_ACE               0x0080  /* INHERITED_ACE, 0x10 
on NT */
 #define        ACE_OWNER                       0x1000
 #define        ACE_GROUP                       0x2000
 #define        ACE_EVERYONE                    0x4000
 
+/* These four are the same as Windows, but with an ACE_ prefix added. */
 #define        ACE_ACCESS_ALLOWED_ACE_TYPE     0x0000
 #define        ACE_ACCESS_DENIED_ACE_TYPE      0x0001
 #define        ACE_SYSTEM_AUDIT_ACE_TYPE       0x0002
@@ -122,6 +142,7 @@ typedef struct acl_info acl_t;
 
 /*
  * These are only applicable in a CIFS context.
+ * Here again, same as Windows, but with an ACE_ prefix added.
  */
 #define        ACE_ACCESS_ALLOWED_COMPOUND_ACE_TYPE            0x04
 #define        ACE_ACCESS_ALLOWED_OBJECT_ACE_TYPE              0x05
_______________________________________________
svn-src-all@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to