Subject: subset of ACLs functionality

From: [email-masked]
Date: Tue, 16 Jul 2002 15:19:36 -0700

About a year ago, we talked about whether it was really necessary to
implement the full-blown ACL spec as defined in Section 5.9 of RFC
3010.  We decided it probably wasn't, and I volunteered to go off and
write up some text that lets the server implement a subset of what's
in the RFC.  Well, I finally got it written, and here it is.  Thanks
to Bill Ricker from Sun for some very useful feedback on a first
draft.

I've attempted to provide change bars against RFC 3010.  But in
addition to the subsetting changes, I rearranged several chunks of
text for easier understanding and flow.  Text that was moved but not
otherwise edited does not have change bars.

mike
--
5.9.  Access Control Lists

   The NFS ACL attribute is an array of access control entries (ACE).
   There are various access control entry types, as defined in Section	    |
   5.9.1.  The server is able to communicate which ACE types are	    |
   supported by returning the appropriate value within the aclsupport
   attribute.  	Each ACE covers one or more operations on a file or	    |
   directory as described in 5.9.2.  It may also contain one or more	    |
   flags that modify the symantics of the ACE as defiend in Section	    |
   5.9.3.								    |
									    |
   The NFS ACE attribute is defined as follows:

   typedef uint32_t        acetype4;
   typedef uint32_t        aceflag4;
   typedef uint32_t        acemask4;

   struct nfsace4 {
           acetype4        type;
           aceflag4        flag;
           acemask4        access_mask;
           utf8string      who;
   };

   To determine if an ACCESS or OPEN request succeeds, each nfsace4 entry   |
   is processed in order by the server.  Only ACEs which have a "who"
   that matches the requester are considered.  Each ACE is processed
   until all of the bits of the requester's access have been ALLOWED.
   Once a bit (see below) has been ALLOWED by an ACCESS_ALLOWED_ACE, it
   is no longer considered in the processing of later ACEs. If an
   ACCESS_DENIED_ACE is encountered where the requester's mode still has
   unALLOWED bits in common with the "access_mask" of the ACE, the
   request is denied.

   The NFS ACL model is fairly rich.  Some server platforms may		    |
   provide access control functionality that goes beyond the		    |
   Unix-style mode attribute, but which is not as rich as the NFS ACL	    |
   model.  So that users can take advantage of this more limited	    |
   functionality, the server may claim that it supports ACLs as long	    |
   as it follows the guidelines for mapping between its ACL model and	    |
   the NFS ACL model.							    |
									    |
   The situation is complicated by the fact that a server may have	    |
   multiple modules that enforce ACLs.  For example, the enforcement	    |
   for NFS access may be handled differently than the enforcement for	    |
   local access, and both may be different than the enforcement for	    |
   access through other protocols such as SMB.  So it may be useful	    |
   for a server to accept an ACL even if not all of its modules are	    |
   able to support it.							    |
									    |
   The guiding principle in all cases is that the server should not	    |
   accept ACLs that appear to make the file more secure than it really	    |
   is.									    |

5.9.1.  ACE type

   Type         Description
   _____________________________________________________
   ALLOW        Explicitly grants the access defined in
                acemask4 to the file or directory.

   DENY         Explicitly denies the access defined in
                acemask4 to the file or directory.

   AUDIT        LOG (system dependent) any access
                attempt to a file or directory which
                uses any of the access methods specified
                in acemask4.

   ALARM        Generate a system ALARM (system
                dependent) when any access attempt is
                made to a file or directory for the
                access methods specified in acemask4.

   A server need not support all of the above ACE types.  The bitmask	    |
   constants used to represent the above definitions within the		    |
   aclsupport attribute are as follows:					    |

   const ACL4_SUPPORT_ALLOW_ACL    = 0x00000001;
   const ACL4_SUPPORT_DENY_ACL     = 0x00000002;
   const ACL4_SUPPORT_AUDIT_ACL    = 0x00000004;
   const ACL4_SUPPORT_ALARM_ACL    = 0x00000008;

   The semantics of the "type" field follow the descriptions provided
   above.

   The constants used for the type field (acetype4) are as follows:	    |

   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;

   Clients should not attempt to set an ACE unless the server claims	    |
   support for that ACE type.  If the server receives a request to set	    |
   an ACE that it cannot store, it must reject the request with		    |
   NFS4ERR_INVAL.							    |
									    |
   If the server receives a request to set an ACE that it can store	    |
   but cannot enforce, the server's response depends on the type of	    |
   the ACE.  For ALLOW ACEs, the server may accept or reject the	    |
   request.  For DENY, AUDIT, and ALARM ACEs, the server should		    |
   generally reject the request.					    |
									    |
   Example: suppose a server can enforce NFS ACLs for NFS access but	    |
   cannot enforce ACLs for local access.  If arbitrary processes can	    |
   run on the server, then the server should reject any ACLs that	    |
   contain a DENY, AUDIT, or ALARM ACE.  On the other hand, if only	    |
   trusted administrative programs run locally, then the server may	    |
   allow these ACEs.							    |

5.9.2.  ACE Access Mask

   The access_mask field contains values based on the following:

   Access                 Description
   _______________________________________________________________
   READ_DATA              Permission to read the data of the file
   LIST_DIRECTORY         Permission to list the contents of a
                          directory
   WRITE_DATA             Permission to modify the file's data
   ADD_FILE               Permission to add a new file to a
                          directory
   APPEND_DATA            Permission to append data to a file
   ADD_SUBDIRECTORY       Permission to create a subdirectory to a
                          directory
   READ_NAMED_ATTRS       Permission to read the named attributes
                          of a file
   WRITE_NAMED_ATTRS      Permission to write the named attributes
                          of a file
   EXECUTE                Permission to execute a file
   DELETE_CHILD           Permission to delete a file or directory
                          within a directory
   READ_ATTRIBUTES        The ability to read basic attributes
                          (non-acls) of a file
   WRITE_ATTRIBUTES       Permission to change basic attributes
                          (non-acls) of a file

   DELETE                 Permission to Delete the file
   READ_ACL               Permission to Read the ACL
   WRITE_ACL              Permission to Write the ACL
   WRITE_OWNER            Permission to change the owner
   SYNCHRONIZE            Permission to access file locally at the
                          server with synchronous reads and writes

   The bitmask constants used for the access mask field are as follows:

   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_DELETE               = 0x00010000;
   const ACE4_READ_ACL             = 0x00020000;
   const ACE4_WRITE_ACL            = 0x00040000;
   const ACE4_WRITE_OWNER          = 0x00080000;
   const ACE4_SYNCHRONIZE          = 0x00100000;

   Server implementations need not provide the granularity of control	    |
   that is implied by this list of masks.  For example, POSIX-based	    |
   systems might not distinguish APPEND_DATA (the ability to append to	    |
   a file) from WRITE_DATA (the ability to modify existing contents);	    |
   both masks would be tied to a single ``write'' permission.  When	    |
   such a server returns attributes to the client, it would show both	    |
   APPEND_DATA and WRITE_DATA if and only if the write permission is	    |
   enabled.								    |
									    |
   If a server receives a SETATTR request that it cannot accurately	    |
   implement, it should err in the direction of more restricted		    |
   access.  For example, suppose a server cannot distinguish		    |
   overwriting data from appending new data, as described in the	    |
   previous paragraph.  If a client submits an ACE where APPEND_DATA	    |
   is set but WRITE_DATA is not (or vice versa), the server should	    |
   reject the request with NFS4ERR_INVAL.  Nonetheless, if the ACE has	    |
   type DENY, the server may silently turn on the other bit, so that	    |
   both APPEND_DATA and WRITE_DATA are denied.				    |

5.9.3.  ACE flag

   The "flag" field contains values based on the following descriptions.

   ACE4_FILE_INHERIT_ACE

   Can be placed on a directory and indicates that this ACE should be
   added to each new non-directory file created.

   ACE4_DIRECTORY_INHERIT_ACE

   Can be placed on a directory and indicates that this ACE should be
   added to each new directory created.

   ACE4_INHERIT_ONLY_ACE

   Can be placed on a directory but does not apply to the directory,
   only to newly created files/directories as specified by the above two
   flags.

   ACE4_NO_PROPAGATE_INHERIT_ACE

   Can be placed on a directory. Normally when a new directory is
   created and an ACE exists on the parent directory which is marked
   ACL4_DIRECTORY_INHERIT_ACE, two ACEs are placed on the new directory.
   One for the directory itself and one which is an inheritable ACE for
   newly created directories.  This flag tells the server to not place
   an ACE on the newly created directory which is inheritable by
   subdirectories of the created directory.

   ACE4_SUCCESSFUL_ACCESS_ACE_FLAG

   ACE4_FAILED_ACCESS_ACE_FLAG						    |

   Both indicate for AUDIT and ALARM which state to log the event.  On
   every ACCESS or OPEN call which occurs on a file or directory which
   has an ACL that is of type ACE4_SYSTEM_AUDIT_ACE_TYPE or
   ACE4_SYSTEM_ALARM_ACE_TYPE, the attempted access is compared to the
   ace4mask of these ACLs. If the access is a subset of ace4mask and the
   identifier match, an AUDIT trail or an ALARM is generated.  By
   default this happens regardless of the success or failure of the
   ACCESS or OPEN call.

   The flag ACE4_SUCCESSFUL_ACCESS_ACE_FLAG only produces the AUDIT or
   ALARM if the ACCESS or OPEN call is successful. The
   ACE4_FAILED_ACCESS_ACE_FLAG causes the ALARM or AUDIT if the ACCESS
   or OPEN call fails.

   ACE4_IDENTIFIER_GROUP

   Indicates that the "who" refers to a GROUP as defined under Unix.

   The bitmask constants used for the flag field are as follows:

   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;

   A server need not support any of these flags.  If the server		    |
   supports flags that are similar to, but not exactly the same as,	    |
   these flags, the implementation may define a mapping between the	    |
   protocol-defined flags and the implementation-defined flags.		    |
   Again, the guiding principle is that the file not appear to be more	    |
   secure than it really is.						    |
									    |
   For example, suppose a client tries to set an ACE with		    |
   ACE4_FILE_INHERIT_ACE set but not ACE4_DIRECTORY_INHERIT_ACE.  If	    |
   the server does not support any form of ACL inheritance, the server	    |
   should reject the request with NFS4ERR_INVAL.  If the server		    |
   supports a single "inherit ACE" flag that applies to both files and	    |
   directories, the server may reject the request (i.e., requiring the	    |
   client to set both the file and directory inheritance flags).  The	    |
   server may also accept the request and silently turn on the		    |
   ACE4_DIRECTORY_INHERIT_ACE flag.					    |

5.9.4.  ACE who

   There are several special identifiers ("who") which need to be
   understood universally, rather than in the context of a particular	    |
   DNS domain. Some of these identifiers cannot be understood when an	    |
   NFS client accesses the server, but have meaning when a local
   process accesses the file. The ability to display and modify these
   permissions is permitted over NFS, even if none of the access	    |
   methods on the server understands the identifiers.			    |

   Who                    Description
   _______________________________________________________________
   "OWNER"                The owner of the file.
   "GROUP"                The group associated with the file.
   "EVERYONE"             The world.
   "INTERACTIVE"          Accessed from an interactive terminal.
   "NETWORK"              Accessed via the network.
   "DIALUP"               Accessed as a dialup user to the server.
   "BATCH"                Accessed from a batch job.
   "ANONYMOUS"            Accessed without any authentication.
   "AUTHENTICATED"        Any authenticated user (opposite of
                          ANONYMOUS)
   "SERVICE"              Access from a system service.

   To avoid conflict, these special identifiers are distinguish by an
   appended "@" and should appear in the form "xxxx@" (note: no domain
   name after the "@").  For example: ANONYMOUS@.

---

From: "Noveck, Dave" <[email-masked]>
Date: Wed, 17 Jul 2002 12:44:25 -0700

I wrote: 
> I think your treatment of the issue of ACL security in the last
> two paragraphs of section 5.9.1. is incorrect.  You are saying
> that if I can't enforce ACL's for local access (and there is
> significant local access -- i.e. it's not just root-type access),
> then it is OK to accept ACL's that just have ALLOW entries.
> This is not so, by the general principle that you are going under.
> If I have an ACL that just has one ALLOW ACE and it says 
> [email-masked] can read and write the file, the effect of 
> that ACL is to grant access to dnoveck, but also to deny it 
> to anyone else, the lack of explicit DENY ACEs notwithstanding.  
> So accepting it would make the file seem more secure than it in 
> fact is, unless ... you have a system in which the only local 
> access allowed is by [email-masked], which would certainly 
> be an unusual case. 

It's been pointed out to me that there is an even better example
that shows this issue.  An ACL that consists of zero ACE's should
have the effect of denying all access.  So if you don't apply 
the ACL's (and there are not considerations that justify you 
excluding local access as de minimis from a security point of view), 
you can't accept any ACL's.