NFSv4 ACLs.

From: Edward Tomasz Napierala <trasz_at_FreeBSD.org>
Date: Fri, 29 Aug 2008 15:59:05 +0200
Right now, FreeBSD supports one type of ACLs - so called POSIX.1e
ACLs.  They are natural extension of the traditional Unix
permissions, and for a long time were the only nonproprietary
ACL type in Unix systems.  Times change, and now there are several
problems with them: they never actually become standard (the draft
they are based on was withdrawn); this results in minor
incompatibilities related to e.g. umask handling; they don't fit
into Windows or NFSv4 security model, which makes interoperability
between these systems harder; finally, they don't work with ZFS.

The aim of my GSoC project was to implement NFSv4 ACLs in a similar
way POSIX.1e ACLs are supported.  That was done by extending user
utilities (setfacl(1)/getfacl(1)), libc API and adding neccessary
kernel stuff.  Semantics is supposed to be identical to the one
in SunOS.  There is also a wrapper (distributed separately) that
implements SunOS-compatible acl(2)/facl(2) API, to make porting
applications like Samba easier.

Userland tools - setfacl(1) and getfacl(1) were modified to work with
new ACLs.  The former was slightly restructured to make it more readable.
Most of the setfacl(1) options work the same way with NFS4 ACLs as they
do with POSIX.1e ACLs.  However, things like "-m" or "-x" don't always
fit in a model where ordering of entries matters and there may be more
than one identical entry.  Thus, two more options were added - "-a",
that adds ACL entry at the specified position, and '-x number', which
removes ACL entry at a specified position.  Manual pages were updated
as appropriate.
   
ACL format is the same as SunOS "positional", aka "compact", format.
For example:
   
[trasz_at_traszkan:~]$ getfacl /
# file: /
# owner: root
# group: wheel
            owner_at_:--------------:------:deny
            owner_at_:rwxp---A-W-Co-:------:allow
            group_at_:-w-p----------:------:deny
            group_at_:r-x-----------:------:allow
         everyone_at_:-w-p---A-W-Co-:------:deny
         everyone_at_:r-x---a-R-c--s:------:allow

There is also a "verbose" format, similar - but not identical - to
the SunOS counterpart:

[trasz_at_traszkan:~]$ getfacl -v /
# file: /
# owner: root
# group: wheel
            owner_at_:::deny
            owner_at_:read_data/write_data/execute/append_data/write_attributes/write_xattr/write_acl/write_owner::allow
            group_at_:write_data/append_data::deny
            group_at_:read_data/execute::allow
         everyone_at_:write_data/append_data/write_attributes/write_xattr/write_acl/write_owner::deny
         everyone_at_:read_data/execute/read_attributes/read_xattr/read_acl/synchronize::allow

The ls(1) and chmod(1) utilities were modified to remove POSIX.1e-specific
assumptions.

The following functions were added to the acl(3) API: acl_add_flag_np,
acl_clear_flags_np, acl_create_entry_np, acl_delete_entry_np,
acl_delete_flag_np, acl_get_extended_np, acl_get_flag_np, acl_get_flagset_np,
acl_set_extended_np, acl_set_flagset_np, acl_to_text_np, acl_is_trivial_np,
acl_strip_np, acl_get_brand_np.  The flags part is source compatible with
Darwin, other functions don't have their counterparts there.  Manual
pages were added or modified as neccessary.  There should be no backward
incompatible changes.

As for the libc internals - "struct acl" et al were extended to make room
for additional entries required by NFS4.  Mechanism called "branding" was
introduced to prevent API users from mixing POSIX and NFS4 specific stuff
in a single ACL.  One thing that still needs to be done is to add a few
binary compatibility wrappers; these places are marked with 'XXX' in the
code.  ACL_MAX_ENTRIES was increased from 32 to 204.

Syscall interface remains the same, except for a changed "type" constants,
in order to preserve backwards compatibility with older libc.

VOP_SETACL, VOP_GETACL and VOP_ACLCHECK interface remains the same,
except for modified "struct acl".  VOP_ACCESS changed a little - second
argument was changed from "int" to "vaccess_t"; there should be no
functional changes related to that.  There are differences related to
permissions granularity; as long as VOP_ACCESS implementation simply
passes the "a_mode" parameter to the proper vaccess(9) routine, there
should be no difference in behaviour.

Adding granularity required adding some VWHATEVER defines to sys/vnode.h,
adding several VOP_ACCESS checks in the syscall layer, replacing VADMIN
with more specific permission in UFS and adding a unixify_vaccess()
routine that "strips the granularity" from the mode.  This routine is
used in vaccess(9) and vaccess_acl_posix1e(9).

Filesystem-independent functionality was implemented in kern/subr_acl_nfs4.c.
This includes vaccess_acl_nfs4, routines to recompute ACL after the mode
change, recompute the mode after ACL change, compute inherited ACL, and
check the ACL for correctness.  Implementation is based on
draft-ietf-nfsv4-minorversion1-03, with small tweaks to make it behave
exactly as ZFS does.  Later drafts are unfortunately less specific,
probably to make it possible to actually pass the voting.  Note that
these routines are used only by UFS; ZFS has its own implementation.

Support for NFS4 ACL storage and retrieval was added to ufs/ufs/ufs_acl.c.
Stored ACLs are variable in size, to reduce disk space used.  Other parts
of UFS (ufs_vnops.c and ufs_lookup.c) were modified to implement granularity.

Support for NFS4 ACL storage and retrival was added to ZFS.  It interfaces
with ZFS own ACL mechanisms through wrappers.

Two sets of unit tests were implemented.  First, to test permission checks,
was implemented as a part of fstest, tools/regression/fstest/tests/granular.
Second, an "utility-level" test, is in tools/regression/acltools.  It checks
behaviour from the command-line point of view.  It verifies the correct
behaviour of getfacl(1), setfacl(1), cp(1) and mv(1) utilities (which obviously
requires correct operation of the libc part), as well as recomputing ACLs
on mode change, recomputing mode on ACL change, and ACL inheritance, which
are performed by the kernel.  These tests were used to make sure UFS behaves
exactly the same way ZFS does.  There is also a basic test for POSIX.1e ACLs
as well, to make sure that part of functionality wasn't broken.

The code is in working state.  It can be found in Perforce:

//depot/projects/soc2008/trasz_nfs4acl/...

Patch against yesterdays -CURRENT is at:

http://people.freebsd.org/~trasz/20080827-nfs4acls.diff

I'd like to ask you for review, comments and suggestions.  And testing,
of course - this is just a prototype, but a working one.

This work was done as a Google Summer of Code project.  Thanks, Google :-)
Received on Fri Aug 29 2008 - 12:10:00 UTC

This archive was generated by hypermail 2.4.0 : Wed May 19 2021 - 11:39:34 UTC