Skip to content

Commit

Permalink
Implement NFSv41 ACLs through xattr (#10)
Browse files Browse the repository at this point in the history
This implements NFSv41 (RFC 5661) ACLs in a manner
compatible with vfs_nfs4acl_xattr in Samba and
nfs4xdr-acl-tools.

There are three key areas of change in this commit:
1) NFSv4 ACL management through system.nfs4_acl_xdr xattr.
  Install an xattr handler for "system.nfs4_acl_xdr" that
  presents an xattr containing full NFSv41 ACL structures
  generated through rpcgen using specification from the Samba
  project. This xattr is used by userspace programs to read and
  set permissions.

2) add an i_op->permissions endpoint: zpl_permissions(). This
  is used by the VFS in Linux to determine whether to allow /
  deny an operation. Wherever possible, we try to avoid having
  to call zfs_access(). If kernel has NFSv4 patch for VFS, then
  perform more complete check of avaiable access mask.

3) add capability-based overrides to secpolicy_vnode_access2()
  there are various situations in which ACL may need to be
  overridden based on capabilities. This logic is almost directly
  copied from Linux VFS. For instance, root needs to be able to
  always read / write ACLs (otherwise admin can get locked out
  from files).

This is commit was initially inspired by work from Paul B. Henson
to implement NFSv4.0 (RFC3530) ACLs in ZFS on Linux. Key areas of
divergence are as follows:
- ACL specification, xattr format, xattr name
- Addition of handling for NFSv4 masks from Linux VFS
- Addition of ACL overrides based on capabilities

Signed-off-by: Andrew Walker <[email protected]>
  • Loading branch information
anodos325 authored May 24, 2021
1 parent 0b1ffcc commit ae7e6ca
Show file tree
Hide file tree
Showing 12 changed files with 622 additions and 4 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
modules.order
Makefile
Makefile.in
nfs41acl.h
nfs41acl_xdr.c

#
# Top level generated files specific to this top level dir
Expand Down
1 change: 1 addition & 0 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ cstyle:
@find ${top_srcdir} -name build -prune -o -name zfsd -prune \
-o -type f -name '*.[hc]' \
! -name 'zfs_config.*' ! -name '*.mod.c' \
! -name 'nfs41acl_xdr.c' ! -name 'nfs41acl.h' \
! -name 'opt_global.h' ! -name '*_if*.h' \
! -path './module/zstd/lib/*' \
-exec ${top_srcdir}/scripts/cstyle.pl -cpP {} \+
Expand Down
1 change: 1 addition & 0 deletions include/os/linux/spl/rpc/xdr.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#define _SPL_RPC_XDR_H

#include <sys/types.h>
#include <sys/sysmacros.h>

typedef int bool_t;

Expand Down
2 changes: 2 additions & 0 deletions include/os/linux/zfs/sys/zpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ zpl_chmod_acl(struct inode *ip)
}
#endif /* CONFIG_FS_POSIX_ACL */

extern int zpl_permission(struct inode *ip, int mask);

extern xattr_handler_t *zpl_xattr_handlers[];

/* zpl_ctldir.c */
Expand Down
10 changes: 9 additions & 1 deletion module/Makefile.in
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,14 @@ endif
FMAKE = env -u MAKEFLAGS make $(FMAKEFLAGS)

modules-Linux:
rpcgen -h "@abs_srcdir@/os/linux/zfs/nfs41acl.x" > "@abs_srcdir@/os/linux/zfs/nfs41acl.h"
sed -i "/\<rpc\/rpc.h\>/c\\#include \<rpc\/xdr.h\>" "@abs_srcdir@/os/linux/zfs/nfs41acl.h"
sed -i "9i #include <sys/sysmacros.h>" "@abs_srcdir@/os/linux/zfs/nfs41acl.h"

rpcgen -c "@abs_srcdir@/os/linux/zfs/nfs41acl.x" > "@abs_srcdir@/os/linux/zfs/nfs41acl_xdr.c"
sed -i "5i #pragma GCC diagnostic push" "@abs_srcdir@/os/linux/zfs/nfs41acl_xdr.c"
sed -i "6i #pragma GCC diagnostic ignored \"-Wunused-variable\"" "@abs_srcdir@/os/linux/zfs/nfs41acl_xdr.c"
echo "#pragma GCC diagnostic pop" >> "@abs_srcdir@/os/linux/zfs/nfs41acl_xdr.c"
list='$(SUBDIR_TARGETS)'; for targetdir in $$list; do \
$(MAKE) -C $$targetdir; \
done
Expand Down Expand Up @@ -132,7 +140,7 @@ cppcheck-FreeBSD:
cppcheck: cppcheck-@ac_system@

distdir:
(cd @srcdir@ && find $(ZFS_MODULES) os -name '*.[chS]') | \
(cd @srcdir@ && find $(ZFS_MODULES) os -name '*.[chxS]') | \
while read path; do \
mkdir -p $$distdir/$${path%/*}; \
cp @srcdir@/$$path $$distdir/$$path; \
Expand Down
1 change: 1 addition & 0 deletions module/os/linux/zfs/Makefile.in
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ ccflags-$(CONFIG_SPARC64) += -Wno-unused-value
$(MODULE)-objs += ../os/linux/zfs/abd_os.o
$(MODULE)-objs += ../os/linux/zfs/arc_os.o
$(MODULE)-objs += ../os/linux/zfs/mmp_os.o
$(MODULE)-objs += ../os/linux/zfs/nfs41acl_xdr.o
$(MODULE)-objs += ../os/linux/zfs/policy.o
$(MODULE)-objs += ../os/linux/zfs/trace.o
$(MODULE)-objs += ../os/linux/zfs/qat.o
Expand Down
74 changes: 74 additions & 0 deletions module/os/linux/zfs/nfs41acl.x
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
const ACE4_ACCESS_ALLOWED_ACE_TYPE = 0x00000000;
const ACE4_ACCESS_DENIED_ACE_TYPE = 0x00000001;
const ACE4_SYSTEM_AUDIT_ACE_TYPE = 0x00000002;
const ACE4_SYSTEM_ALARM_ACE_TYPE = 0x00000003;

typedef u_int acetype4;

const ACE4_FILE_INHERIT_ACE = 0x00000001;
const ACE4_DIRECTORY_INHERIT_ACE = 0x00000002;
const ACE4_NO_PROPAGATE_INHERIT_ACE = 0x00000004;
const ACE4_INHERIT_ONLY_ACE = 0x00000008;
const ACE4_SUCCESSFUL_ACCESS_ACE_FLAG = 0x00000010;
const ACE4_FAILED_ACCESS_ACE_FLAG = 0x00000020;
const ACE4_IDENTIFIER_GROUP = 0x00000040;
const ACE4_INHERITED_ACE = 0x00000080;

typedef u_int aceflag4;

const ACEI4_SPECIAL_WHO = 0x00000001;

typedef u_int aceiflag4;

const ACE4_SPECIAL_OWNER = 1;
const ACE4_SPECIAL_GROUP = 2;
const ACE4_SPECIAL_EVERYONE = 3;
const ACE4_SPECIAL_INTERACTIVE = 4;
const ACE4_SPECIAL_NETWORK = 5;
const ACE4_SPECIAL_DIALUP = 6;
const ACE4_SPECIAL_BATCH = 7;
const ACE4_SPECIAL_ANONYMOUS = 8;
const ACE4_SPECIAL_AUTHENTICATED = 9;
const ACE4_SPECIAL_SERVICE = 10;

const ACE4_READ_DATA = 0x00000001;
const ACE4_LIST_DIRECTORY = 0x00000001;
const ACE4_WRITE_DATA = 0x00000002;
const ACE4_ADD_FILE = 0x00000002;
const ACE4_APPEND_DATA = 0x00000004;
const ACE4_ADD_SUBDIRECTORY = 0x00000004;
const ACE4_READ_NAMED_ATTRS = 0x00000008;
const ACE4_WRITE_NAMED_ATTRS = 0x00000010;
const ACE4_EXECUTE = 0x00000020;
const ACE4_DELETE_CHILD = 0x00000040;
const ACE4_READ_ATTRIBUTES = 0x00000080;
const ACE4_WRITE_ATTRIBUTES = 0x00000100;
const ACE4_WRITE_RETENTION = 0x00000200;
const ACE4_WRITE_RETENTION_HOLD = 0x00000400;

const ACE4_DELETE = 0x00010000;
const ACE4_READ_ACL = 0x00020000;
const ACE4_WRITE_ACL = 0x00040000;
const ACE4_WRITE_OWNER = 0x00080000;
const ACE4_SYNCHRONIZE = 0x00100000;

typedef u_int acemask4;

struct nfsace4i {
acetype4 type;
aceflag4 flag;
aceiflag4 iflag;
acemask4 access_mask;
u_int who;
};

const ACL4_AUTO_INHERIT = 0x00000001;
const ACL4_PROTECTED = 0x00000002;
const ACL4_DEFAULTED = 0x00000004;

typedef u_int aclflag4;

struct nfsacl41i {
aclflag4 na41_flag;
nfsace4i na41_aces<>;
};
44 changes: 42 additions & 2 deletions module/os/linux/zfs/policy.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
#include <sys/policy.h>
#include <linux/security.h>
#include <linux/vfs_compat.h>
#include <sys/zfs_znode.h>

/*
* The passed credentials cannot be directly verified because Linux only
Expand Down Expand Up @@ -103,13 +104,52 @@ secpolicy_sys_config(const cred_t *cr, boolean_t checkonly)
* Like secpolicy_vnode_access() but we get the actual wanted mode and the
* current mode of the file, not the missing bits.
*
* Enforced in the Linux VFS.
* If filesystem is using NFSv4 ACLs, validate the current mode
* and the wanted mode are the same, otherwise access fails.
*
* If using POSIX ACLs or no ACLs, enforced in the Linux VFS.
*/
int
secpolicy_vnode_access2(const cred_t *cr, struct inode *ip, uid_t owner,
mode_t curmode, mode_t wantmode)
{
return (0);
mode_t remainder = ~curmode & wantmode;
if ((ITOZSB(ip)->z_acl_type != ZFS_ACLTYPE_NFSV4) ||
(remainder == 0)) {
return (0);
}

/*
* short-circuit if root
*/
if (capable(CAP_SYS_ADMIN)) {
return (0);
}

/*
* There are some situations in which capabilities
* may allow overriding the DACL.
*/
if (S_ISDIR(ip->i_mode)) {
if (!(wantmode & S_IWUSR) &&
capable(CAP_DAC_READ_SEARCH)) {
return (0);
}
if (capable(CAP_DAC_OVERRIDE)) {
return (0);
}
return (EACCES);
}

if ((wantmode == S_IRUSR) && capable(CAP_DAC_READ_SEARCH)) {
return (0);
}

if (!(remainder & S_IXUSR) && capable(CAP_DAC_OVERRIDE)) {
return (0);
}

return (EACCES);
}

/*
Expand Down
14 changes: 13 additions & 1 deletion module/os/linux/zfs/zfs_vfsops.c
Original file line number Diff line number Diff line change
Expand Up @@ -358,12 +358,17 @@ acltype_changed_cb(void *arg, uint64_t newval)
zfsvfs_t *zfsvfs = arg;

switch (newval) {
case ZFS_ACLTYPE_NFSV4:
case ZFS_ACLTYPE_OFF:
zfsvfs->z_acl_type = ZFS_ACLTYPE_OFF;
zfsvfs->z_sb->s_flags &= ~SB_POSIXACL;
#ifdef SB_NFSV4ACL
zfsvfs->z_sb->s_flags &= ~SB_NFSV4ACL;
#endif
break;
case ZFS_ACLTYPE_POSIX:
#ifdef SB_NFSV4ACL
zfsvfs->z_sb->s_flags &= ~SB_NFSV4ACL;
#endif
#ifdef CONFIG_FS_POSIX_ACL
zfsvfs->z_acl_type = ZFS_ACLTYPE_POSIX;
zfsvfs->z_sb->s_flags |= SB_POSIXACL;
Expand All @@ -372,6 +377,13 @@ acltype_changed_cb(void *arg, uint64_t newval)
zfsvfs->z_sb->s_flags &= ~SB_POSIXACL;
#endif /* CONFIG_FS_POSIX_ACL */
break;
case ZFS_ACLTYPE_NFSV4:
zfsvfs->z_acl_type = ZFS_ACLTYPE_NFSV4;
zfsvfs->z_sb->s_flags &= ~SB_POSIXACL;
#ifdef SB_NFSV4ACL
zfsvfs->z_sb->s_flags |= SB_NFSV4ACL;
#endif
break;
default:
break;
}
Expand Down
3 changes: 3 additions & 0 deletions module/os/linux/zfs/zpl_inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -720,6 +720,7 @@ const struct inode_operations zpl_inode_operations = {
#endif /* HAVE_SET_ACL */
.get_acl = zpl_get_acl,
#endif /* CONFIG_FS_POSIX_ACL */
.permission = zpl_permission,
};

const struct inode_operations zpl_dir_inode_operations = {
Expand Down Expand Up @@ -753,6 +754,7 @@ const struct inode_operations zpl_dir_inode_operations = {
#endif /* HAVE_SET_ACL */
.get_acl = zpl_get_acl,
#endif /* CONFIG_FS_POSIX_ACL */
.permission = zpl_permission,
};

const struct inode_operations zpl_symlink_inode_operations = {
Expand Down Expand Up @@ -792,6 +794,7 @@ const struct inode_operations zpl_special_inode_operations = {
#endif /* HAVE_SET_ACL */
.get_acl = zpl_get_acl,
#endif /* CONFIG_FS_POSIX_ACL */
.permission = zpl_permission,
};

dentry_operations_t zpl_dentry_operations = {
Expand Down
3 changes: 3 additions & 0 deletions module/os/linux/zfs/zpl_super.c
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,9 @@ __zpl_show_options(struct seq_file *seq, zfsvfs_t *zfsvfs)
case ZFS_ACLTYPE_POSIX:
seq_puts(seq, ",posixacl");
break;
case ZFS_ACLTYPE_NFSV4:
seq_puts(seq, ",nfs4acl");
break;
default:
seq_puts(seq, ",noacl");
break;
Expand Down
Loading

0 comments on commit ae7e6ca

Please sign in to comment.