On 7 March Andrew Deason submitted a patch to OpenAFS documenting the existing behavior of the OpenAFS fileserver when computing Anonymous and Caller Access Rights if the IPv4 address from which the RXAFS RPC was received matches a PTS host entry and that PTS entry matches an Access Control Entry (ACE).

https://gerrit.openafs.org/#/c/15340/

Quoting Andrew's submission to the fs_setacl man page:

   "Combining _Negative rights_ granted from machine entries (IP
   addresses) and _Normal rights_ granted from non-machine entries (or
   vice versa) will generally not work as expected. Permissions granted
   by machine entries and by non-machine entries are calculated
   separately, and both sets of permissions are given to an accessing
   user. For example, if permissions are granted to an authenticated
   user or group (or _system:anyuser_), you cannot remove those
   permissions from specific hosts by adding machine entries to a group
   in an ACL in the _Negative rights_> section."

The IBM AFS Administrator's Guide "Protecting Data in AFS" section states:

   "When determining what type of access to grant to a user, theFile
   Server first compiles a set of permissions by examiningall of the
   entries in the Normal rights section of the ACL. Itthen subtracts
   any permissions associated with the user (orwith groups to which the
   user belongs) on the Negative rightssection of the ACL. Therefore,
   negative permissions alwayscancel out normal permissions."

IBM/Transarc AFS 3.2 introduced the granting of permissions based upon the host's IPv4 address in addition to those granted to the caller. The implementation evaluates the caller's rights independently of the host's rights and then ORs the results. This approach violates the statement that negative permissions always cancel out normal (aka positive) permissions. If a caller is granted "read" but there is a matching negative "read" ACE (aka permission) for the host, the negative "read" ACE is ignored. Likewise if "lookup" is granted to the host but the caller matches a negative "lookup" ACE, then the caller's negative "lookup" ACE is ignored.

The problem can be demonstrated with a couple of examples.   First, lets define some PTS entities and membership relations:

 * user: jane = 1000
     o member: system:authusers
     o member: system:anyuser
     o member: no-admin
 * user: 128.66.0.130 = 2000
     o member: local-hosts
 * group: no-admin = -100
     o member: jane
 * group: local-hosts = -500
     o member: 128.66.0.130

Example 1:  RXAFS RPC received from a host that is not a member of local-hosts

ACL

 * system:anyuser: l; -none
 * system:authuser: lrk; -none
 * jane: none; -r
 * local-hosts: r; -none

Rights:

 * system:anyuser: lookup
 * system:authuser: lookup, read, lock
 * jane: lookup, lock

When "jane" accesses a file with this ACL the granted rights will be "lk" because the negative read permission cancels the positive read permission granted by the membership in the system:authuser group.


Example 2: RXAFS RPC received from a host that is a member of local-hosts

ACL

 * system:anyuser: l; -none
 * system:authuser: lrk; -none
 * jane: none; -r
 * local-hosts: r; -none

Rights:

 * system:anyuser: lookup, read
 * system:authuser: lookup, read, lock
 * jane: lookup, read, lock

In this case, even though "jane" is denied the "read" permission granted to members of "system:authuser" because of the negative "read" in the "jane" ACE she is granted the permission because of the positive read permission granted to "local-hosts" members. The granting of "read" permission to "jane" is an unexpected result!


Example 3: RXAFS RPC received from a host that is not a member of local-hosts

ACL

 * system:anyuser: l; -none
 * system:authuser: lrk; -none
 * jane: lrkwid; -none
 * local-hosts: none; -wida

Rights:

 * system:anyuser: lookup
 * system:authuser: lookup, read, lock
 * jane: lookup, read, lock, write, insert, delete

In this case, "jane" is granted all of the permissions other than "admin".


Example 4: RXAFS RPC received from a host that is a member of local-hosts

ACL

 * system:anyuser: l; -none
 * system:authuser: lrk; -none
 * jane: lrkwid; -none
 * local-hosts: none; -wida

Rights:

 * system:anyuser: lookup
 * system:authuser: lookup, read, lock
 * jane: lookup, read, lock, write, insert, delete

In this case, "jane" is granted all of the permissions other than "admin".   However, because the RPC was issued from a host that is a member of "local-hosts" the expected result would be "jane" receiving only the "lookup, read, lock" rights.    The granting of "write, insert and delete" permission is an unexpected outcome!


In examples 2 and 4 rights are granted to the caller that would appear to be contrary to the explicit use of negative rights in the access control entries.  The example 4 use case might represent client systems which are intended to be read-only consumers of the /afs content but which might be vulnerable to exploitation.   Example 2 is intended to always deny read privileges to "jane" but if she can gain access to the host with IPv4 address 128.66.0.130 she can bypass those restrictions.

This behavior feels like a bug to me. A bug that has been present since the release of AFS 3.2 more than thirty years ago but a bug nevertheless.  I propose that OpenAFS fix the broken behavior so that ACL interpretation is consistent with the documentation inherited from IBM.

Two commits submitted to Gerrit will enforce negative access rights.

https://gerrit.openafs.org/#/c/15344/
https://gerrit.openafs.org/#/c/15345/


History:

AFS 3.1 and earlier did not have a concept of IP or Host ACLs.   That functionality was introduced in AFS 3.2.  However, the implementation that is present in the release version of AFS 3.2 and in use to this day might not have been the original implementation.    Today's fileserver issues separate calls to the protection service to obtain the caller's Current Protection Set (CPS) and the host's CPS.   The fileserver then computes the caller rights by comparing the caller's CPS to the object's ACL; and it computes the host's rights by comparing the host's CPS to the object's ACL.   The fileserver then grants the caller all rights granted to both the caller and the host.

The host's CPS is obtained by the fileserver by issuing the PR_GetHostCPS RPC.  PR_GetHostCPS differs from PR_GetCPS in that it matches the host's IPv4 address using wildcard pattern matching when searching for a matching PTS ID.  It also does not treat the PTS entity as a member of the system:anyuser group.

Before PR_GetHostCPS was added to the protection service another RPC was introduced, PR_GetCPS2 which obtained the CPS for the combination of a caller and a host. An implementation of IP ACLs that used PR_GetCPS2 would have behaved as expected because the all of the positive rights would have been computed and from those all of the negative rights would have been removed.  I do not have access to the source code for a version of the fileserver that used the PR_GetCPS2 RPC but I can imagine that a likely implementation would have issued a PR_GetCPS2 call for (anonymous, host) and (caller, host).   The additional load these RPCs placed on the protection service might have been deemed too expensive.  As a result, the alternative approach using the PR_GetHostCPS was introduced.

Whatever the motivation for PR_GetHostCPS in preference to PR_GetCPS2, the existing implementation does not interpret ACLs in a manner that permits negative rights to be superior to positive rights.   Perhaps it didn't occur to the implementer that there was a problem; and it didn't occur to anyone that a simple change to acl_checkRights() and the fileserver GetRights() function would permit computing the expected results.


Proposal:

I propose that OpenAFS treat the current behavior as a bug.  The use of negative rights is discouraged because they are hard to analyze.  It is hoped that their use is rare.  If negative rights are not in use, then changing the behavior when IP ACLs exist will not alter the computed outcome.  However, if negative rights are in use, they are likely being used because it wasn't easy to limit the access any other way.  In which case, granting more access then was specified is problematic.   A CVE can be published to document the existing behavior and the behavior as it will appear beginning with a specific version of the fileserver.

If required, a configuration option can be provided to enable the AFS 3.2 behavior until all of the fileservers within a cell have been updated.   I discourage using a configuration option to enable the stricter interpretation of ACLs as that will result in some sites being vulnerable when they did not intend to be.


Jeffrey Altman

P.S. I am writing this letter publicly as opposed to sending it to the openafs-security queue because the behavior in question is public and Andrew's proposed patch to the OpenAFS man pages is public.

Attachment: smime.p7s
Description: S/MIME Cryptographic Signature

Reply via email to