ACL semantics -- consistency with existing ACL systems?

Andreas Gruenbacher a.gruenbacher at infosys.tuwien.ac.at
Sun Oct 24 09:52:56 GMT 1999


Robert Watson wrote:
> 
> I am part way into implementing POSIX.1e ACLs in FreeBSD, and had a couple
> of questions.  During my first pass, I plan to largely implement the same
> ACL library functions as found in Solaris and Linux (the two reference
> implementations I have access to) and put the POSIX.1e interface on top.
> There are some inconsistencies between POSIX.1e behavior and these
> behaviors.  Given that this is only a first pass, and that I haven't had a
> chance to use either a strict POSIX.1e implementation or the Solaris/Linux
> ones in any depth, it's possible that my concerns are due to
> misunderstanding.  Either way, I'd love suggestions and comments :-).
> Unfortunately, I don't have access to an IRIX box so couldn't look at the
> implementation there.
> 
> Both platforms provide acl() and facl() syscalls that both get and set
> ACLs by path or file descriptor, respectively.  The calls work like this
> (Solaris):
> 
>      #include <sys/acl.h>
> 
>      int  acl(char  *pathp,  int  cmd,  int  nentries,   aclent_t
>      *aclbufp)
> 
>      int  facl(int  fildes,  int  cmd,  int  nentries,   aclent_t
>      *aclbufp)
> 
> This is different from POSIX.1e which defines the setting and retrieval of
> ACLs for directories/etc as either the access ACL (ACL_TYPE_ACCESS) or
> default ACL (ACL_TYPE_DEFAULT).  Both Linux and Solaris combine the
> contents of the two ACLs by adding a flag to the type in each acl_entry_t
> to indicate that a given entry is a default entry, and not the base entry.
> For compatibility reasons, I would like to support both the POSIX.1e and
> Solaris/Linux interfaces to the ACLs.
> 
> My plan was to wrap POSIX.1e around the simpler acl/facl interface, but it
> raises some consistency problems because of the lack of seperation between
> default and access ACLs.  Suppose, for example, a process reads in the
> default ACL of a directory, and then wants to write it out again.  In
> POSIX.1e this is described by the series of calls acl_get_file() and
> acl_set_file() with ACL_TYPE_DEFAULT set on both calls.  If this is passed
> down to acl/facl, then the entire ACL will be read and written in both
> cases.  So we'd actually get:
> 
> acl_get_file()
>         acl()           # read in both acls
> process modifies acl
> acl_set_file()
>         acl()           # read in both acls
>         acl()           # write out both acls with one changed
> 
> So in acl_set_file() on just one of the two ACLs, there is an opportunity
> for a race condition--because acl_set_file() doesn't want to remove the
> access portion of the ACL from the file, it must read in the existing ACL,
> merge the changes (overwrite the existing defaul ACL) and then write the
> whole thing out.  The result is that, given a second process doing a
> similar thing at the same time, you can end up with an unfortunate
> arrangement of having it be as though one process hadn't made the changes.
> I.e.,
> 
>         p1                              p2
>         acl_get_file()
>                 acl()
>                                         acl_get_file()
>                                         acl()
>         acl_set_file()
>                 acl()
>                                         acl_set_file()
>                                         acl()
>                 acl()
>                                         acl()
> 
> I.e., if p1 was modifying the default acl, and p2 the access acl, p2 will
> stomp on p1's changes even though in theory they were independent.  In the
> event that they were both restricting access to the directory in question,
> this could result in a user having greater rights on the directory than
> was intended--clearly undesirable.
> 
> I suppose I could code the posix1e routines to use advisory locking during
> multiple calls to acl() in the same library call, but that doesn't seem
> like a great idea.  The correct answer is probably to make acl/facl aware
> of different types of ACLs on the same object.  It seems unfortunately as
> though reversing the arrangement to wrap Solaris/Linux around POSIX.1e
> also suffers from a race, so the solution may end up being to add a new
> argument to acl/facl, or to have four syscalls (which is getting a little
> excessive).  I could also add a couple more cmd constants but none of
> these solutions seems clean.
> 
> Another side effect of the storage of both ACLs in the same structure is
> that routines such as aclcheck and aclsort have mildly different
> semantics.

It may be a good idea to specify in the cmd argument to acl() / facl() which
types of ACLs should be retrieved/set, like:

  ACL_SETACL = 0x1003
    ACL_SETACL_ACCESS  = 0x1001
    ACL_SETACL_DEFAULT = 0x1002
  ACL_GETACL = 0x2003
    ACL_GETACL_ACCESS  = 0x2001
    ACL_GETACL_DEFAULT = 0x2002

There may be other types of ACLs like default directory ACLs, for other
filesystems you may want to support some day. Also, an integer of uid/gid (as
used in the current Solaris/Linux implementations) is too small for some
user/group identifiers (NT, OSF/DCE, ...).

The effects of these additional issues on acl_check isn't defined at all.

> My other thought on the issue was that in my original first pass, I
> required the user process to only submit ordered ACLs, allowing the
> verification of the validity of the ACL to be performed in linear time in
> the kernel, as opposed to the nlogn time if the kernel is responsible for
> dealing with the ordering, etc, to check for unique uids/gids/etc.  This
> is not compatible at a syscall level with Linux and Solaris as they
> require the kernel to verify ACL validity on unordered ACLs.  Clearly
> someone has to do the sorting for the verification sometime (or risk an
> n^2 verification, or use hashes?) but I thought it better to dump the
> burden on the user process (possibly via the library).  Has anyone had any
> thoughts on this one?

I don't know how Solaris really does it (where the sorting happens) but I doubt
they do it in kernel space.

On Linux, acl() and facl() are library calls; with corresponding acl_sorted()
and facl_sorted() syscalls.

> Solaris assigns a maximum ACL length of 1024 entries--Linux places a bound
> based on the block size.  Is this a limit people actually run into?
> Without user-defined groups in UNIX, I could see this as a possible
> problem in some situations, but given the limits of groups under UNIX,
> perhaps that isn't a solution either.
> 
> This also raises an issue with the POSIX.1e interface: acl_get_file()
> provides an argument to determine whether the access acl or default acl
> should be retrieved, if the path provided points to a directory.
> acl_get_fd() does not provide this option, under the (false) assumption
> that it is not possible to have an fd open for a directory?  My temptation
> is to modify the arguments of acl_get_fd to allow for this additional
> argument in the style of acl_get_file().  Has anyone else tried to address
> this?
> 
>   Robert N M Watson
> 
> robert at fledge.watson.org              http://www.watson.org/~robert/
> PGP key fingerprint: AF B5 5F FF A6 4A 79 37  ED 5F 55 E9 58 04 6A B1
> TIS Labs at Network Associates, Safeport Network Services


Andreas

------------------------------------------------------------------------
 Andreas Gruenbacher, Vienna University of Technology
 a.gruenbacher at infosys.tuwien.ac.at
 Contact information: http://www.infosys.tuwien.ac.at/~agruenba
To Unsubscribe: send mail to majordomo at cyrus.watson.org
with "unsubscribe posix1e" in the body of the message



More information about the posix1e mailing list