aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--config/kernel-inode-permission.m429
-rw-r--r--config/kernel.m42
-rw-r--r--module/os/linux/zfs/zfs_acl.c70
-rw-r--r--module/os/linux/zfs/zfs_vnops_os.c2
4 files changed, 102 insertions, 1 deletions
diff --git a/config/kernel-inode-permission.m4 b/config/kernel-inode-permission.m4
new file mode 100644
index 000000000..ba9ff5d43
--- /dev/null
+++ b/config/kernel-inode-permission.m4
@@ -0,0 +1,29 @@
+AC_DEFUN([ZFS_AC_KERNEL_SRC_PERMISSION], [
+ dnl #
+ dnl # 5.12 API change that added the struct user_namespace* arg
+ dnl # to the front of this function type's arg list.
+ dnl #
+ ZFS_LINUX_TEST_SRC([permission_userns], [
+ #include <linux/fs.h>
+ #include <linux/sched.h>
+
+ int inode_permission(struct user_namespace *userns,
+ struct inode *inode, int mask) { return 0; }
+
+ static const struct inode_operations
+ iops __attribute__ ((unused)) = {
+ .permission = inode_permission,
+ };
+ ],[])
+])
+
+AC_DEFUN([ZFS_AC_KERNEL_PERMISSION], [
+ AC_MSG_CHECKING([whether iops->permission() takes struct user_namespace*])
+ ZFS_LINUX_TEST_RESULT([permission_userns], [
+ AC_MSG_RESULT(yes)
+ AC_DEFINE(HAVE_IOPS_PERMISSION_USERNS, 1,
+ [iops->permission() takes struct user_namespace*])
+ ],[
+ AC_MSG_RESULT(no)
+ ])
+])
diff --git a/config/kernel.m4 b/config/kernel.m4
index d1d3dede1..639d18377 100644
--- a/config/kernel.m4
+++ b/config/kernel.m4
@@ -82,6 +82,7 @@ AC_DEFUN([ZFS_AC_KERNEL_TEST_SRC], [
ZFS_AC_KERNEL_SRC_MKDIR
ZFS_AC_KERNEL_SRC_LOOKUP_FLAGS
ZFS_AC_KERNEL_SRC_CREATE
+ ZFS_AC_KERNEL_SRC_PERMISSION
ZFS_AC_KERNEL_SRC_GET_LINK
ZFS_AC_KERNEL_SRC_PUT_LINK
ZFS_AC_KERNEL_SRC_TMPFILE
@@ -193,6 +194,7 @@ AC_DEFUN([ZFS_AC_KERNEL_TEST_RESULT], [
ZFS_AC_KERNEL_MKDIR
ZFS_AC_KERNEL_LOOKUP_FLAGS
ZFS_AC_KERNEL_CREATE
+ ZFS_AC_KERNEL_PERMISSION
ZFS_AC_KERNEL_GET_LINK
ZFS_AC_KERNEL_PUT_LINK
ZFS_AC_KERNEL_TMPFILE
diff --git a/module/os/linux/zfs/zfs_acl.c b/module/os/linux/zfs/zfs_acl.c
index 351e4dad7..b70691ab3 100644
--- a/module/os/linux/zfs/zfs_acl.c
+++ b/module/os/linux/zfs/zfs_acl.c
@@ -863,6 +863,26 @@ zfs_unix_to_v4(uint32_t access_mask)
return (new_mask);
}
+
+static int
+zfs_v4_to_unix(uint32_t access_mask, int *unmapped)
+{
+ int new_mask = 0;
+
+ *unmapped = access_mask &
+ (ACE_WRITE_OWNER | ACE_WRITE_ACL | ACE_DELETE);
+
+ if (access_mask & WRITE_MASK)
+ new_mask |= S_IWOTH;
+ if (access_mask & ACE_READ_DATA)
+ new_mask |= S_IROTH;
+ if (access_mask & ACE_EXECUTE)
+ new_mask |= S_IXOTH;
+
+ return (new_mask);
+}
+
+
static void
zfs_set_ace(zfs_acl_t *aclp, void *acep, uint32_t access_mask,
uint16_t access_type, uint64_t fuid, uint16_t entry_type)
@@ -2399,6 +2419,53 @@ zfs_has_access(znode_t *zp, cred_t *cr)
return (B_TRUE);
}
+/*
+ * Simplified access check for case where ACL is known to not contain
+ * information beyond what is defined in the mode. In this case, we
+ * can pass along to the kernel / vfs generic_permission() check, which
+ * evaluates the mode and POSIX ACL.
+ *
+ * NFSv4 ACLs allow granting permissions that are usually relegated only
+ * to the file owner or superuser. Examples are ACE_WRITE_OWNER (chown),
+ * ACE_WRITE_ACL(chmod), and ACE_DELETE. ACE_DELETE requests must fail
+ * because with conventional posix permissions, right to delete file
+ * is determined by write bit on the parent dir.
+ *
+ * If unmappable perms are requested, then we must return EPERM
+ * and include those bits in the working_mode so that the caller of
+ * zfs_zaccess_common() can decide whether to perform additional
+ * policy / capability checks. EACCES is used in zfs_zaccess_aces_check()
+ * to indicate access check failed due to explicit DENY entry, and so
+ * we want to avoid that here.
+ */
+static int
+zfs_zaccess_trivial(znode_t *zp, uint32_t *working_mode, cred_t *cr)
+{
+ int err, mask;
+ int unmapped = 0;
+
+ ASSERT(zp->z_pflags & ZFS_ACL_TRIVIAL);
+
+ mask = zfs_v4_to_unix(*working_mode, &unmapped);
+ if (mask == 0 || unmapped) {
+ *working_mode = unmapped;
+ return (unmapped ? SET_ERROR(EPERM) : 0);
+ }
+
+#if defined(HAVE_IOPS_PERMISSION_USERNS)
+ err = generic_permission(cr->user_ns, ZTOI(zp), mask);
+#else
+ err = generic_permission(ZTOI(zp), mask);
+#endif
+ if (err != 0) {
+ return (SET_ERROR(EPERM));
+ }
+
+ *working_mode = unmapped;
+
+ return (0);
+}
+
static int
zfs_zaccess_common(znode_t *zp, uint32_t v4_mode, uint32_t *working_mode,
boolean_t *check_privs, boolean_t skipaclchk, cred_t *cr)
@@ -2450,6 +2517,9 @@ zfs_zaccess_common(znode_t *zp, uint32_t v4_mode, uint32_t *working_mode,
return (SET_ERROR(EPERM));
}
+ if (zp->z_pflags & ZFS_ACL_TRIVIAL)
+ return (zfs_zaccess_trivial(zp, working_mode, cr));
+
return (zfs_zaccess_aces_check(zp, working_mode, B_FALSE, cr));
}
diff --git a/module/os/linux/zfs/zfs_vnops_os.c b/module/os/linux/zfs/zfs_vnops_os.c
index ece7c373e..b65728f0d 100644
--- a/module/os/linux/zfs/zfs_vnops_os.c
+++ b/module/os/linux/zfs/zfs_vnops_os.c
@@ -474,7 +474,7 @@ zfs_lookup(znode_t *zdp, char *nm, znode_t **zpp, int flags, cred_t *cr,
*/
if ((error = zfs_zaccess(*zpp, ACE_EXECUTE, 0,
- B_FALSE, cr))) {
+ B_TRUE, cr))) {
zrele(*zpp);
*zpp = NULL;
}