Subject: [nfsv4] Issue90: Drop execution support from ACCESS operation

From: Mike Eisler <[email-masked]>
Date: Wed, 12 Jul 2006 14:30:28 -0700 (PDT)

http://www.opengroup.org/onlinepubs/009695399/functions/access.html#tag_03_10

says:

  If the process has appropriate privileges, an 
  implementation may indicate success for X_OK even 
  if none of the execute file permission bits are set.

Several NFS servers and several operating environments
claiming conformance to the UNIX standard will return
success for X_OK if the uid of the called is zero, and
none of the execute file permission bits are set.

Interestingly, several of the operating enviroments
that return success for X_OK on files with execute bits
all off, but uid 0, won't let uid 0 execute the file.
Whether the file is on a local or remote file system.

Furthermore, even with POSIX ACLs on local files, if the base
mode has no execute bits, but an ACE that gives
another user execute access, that user still cannot execute.

So on most operating systems the exec family of system calls ignores
what access() and ACLs say on most operating systems. 
Actually I know of no operating
system that doesn't ignore access().

Given this reality, life would be simpler for server
implementers and their customers if the NFSv4.x protocol
deleted ACCESS4_EXECUTE. An NFS client that wants to see
if an exec system call can send an ACCESS with ACCESS4_READ
set. If the file has a executable bit set and not a read bit
set, ACCESS can still return success.

There is a downside. Consider the file with
no ACL, no read bits, and just x bits. In NFSv[34], ACCESS 
would be used to block the cp command at the client level,
but the file could still be loaded for execution via OPEN.
Whatever; NFS is not a software copy protection scheme. Let
clients figure it out: they can interpret mode and/or ACL
to implement copy protection or not.

_______________________________________________
nfsv4 mailing list
[email-masked]
https://www1.ietf.org/mailman/listinfo/nfsv4

---

From: Trond Myklebust <[email-masked]>
Date: Thu, 13 Jul 2006 01:49:08 -0400

> another user execute access, that user still cannot execute.

...but allowing/denying execute access to a particular user or group via
an ACL _is_ possible, and has definite value. For instance:

[trondmy@tid trondmy]$ getfacl a.out

# file: a.out
# owner: foo
# group: bar
user::rwx
user:baz:r-x           #effective:r-x
group::r-x              #effective:r-x
mask:r-x
other:r--

If user 'baz' is not a member of group 'bar', then only the ACL will
allow execute rights.

> system that doesn't ignore access().

Wrong!

Linux for one definitely looks at access/ACLs in order to figure out if
the user has execute permissions. The execve() function performs an
additional check on top of the access/ACL to figure out if at least one
of the execute mode bits are set, but that is a _mask_ that is applied
on top of the access/ACL permissions.
This is _not_ a particularity of the NFS client, BTW. The exact same
checks for access/ACLs + at least one execute bit is applied to local
filesystems which support POSIX acls.

I've no idea what Solaris does, but I suspect that they do the same.
Otherwise, what use would the execute ACL be?

> to implement copy protection or not.

Kiss both client and server performance goodbye if clients have to start
sending GETACL requests every time they need to figure out if the user
has execute access.

Cheers,
  Trond

---

From: Mike Eisler <[email-masked]>
Date: Wed, 12 Jul 2006 23:42:51 -0700 (PDT)

> If user 'baz' is not a member of group 'bar', then only the ACL will

You haven't shown that a.out is executable by baz.
I repeated a similar experiment on a non-Linux operating
system (name hidden to protect the guilty), and
execve() refuses to execute.

> on top of the access/ACL permissions.

Operating systems appear to, at best, check if the user
has execute access according to extended permission, and then
*ignore* the access() result if there is no execute bit set in
base mode. Is that an incorrect statement? My guess is most
send an over the network ACCESS call, and then check the mode
bits to ignore the results.  

> This is _not_ a particularity of the NFS client, BTW. The exact same

Right. It is a problem in the operating systems.

> filesystems which support POSIX acls.
[...]
> sending GETACL requests every time they need to figure out if the

Just as clients maintain a cache
of ACCESS results they can also maintain a cache of locally
determined execute access results. Since executables are rarely
updated, the change attribute will tend to be static, and so a
cache entry need rarely be updated.

---

From: Trond Myklebust <[email-masked]>
Date: Thu, 13 Jul 2006 09:32:48 -0400

> execve() refuses to execute.

Sigh...

In window 1:

trondmy@lade ~$ id
uid=520(trondmy) gid=100(users) groups=4(adm),20(dialout),24(cdrom),25(floppy),29(audio),30(dip),40(src),44(video),46(plugdev),100(users),106(lpadmin),110(scanner),112(admin),1000(trondmy)
trondmy@lade ~$ cp -ip a.out /export/ext3/trondmy/
trondmy@lade ~$ cd /export/ext3/trondmy/
trondmy@lade trondmy$ chmod og-x a.out
trondmy@lade trondmy$ ls -l /export/ext3/trondmy
total 8
-rwxr--r-- 1 trondmy users 7550 2006-07-13 08:38 a.out

In window 2:

trondmy@lade ~$ sudo su baz
baz@lade:/home/trondmy$ id
uid=1001(baz) gid=1001(baz) groups=1001(baz)
baz@lade:/home/trondmy$ cd /tmp/
baz@lade:/tmp$ /export/ext3/trondmy/a.out
bash: /export/ext3/trondmy/a.out: Permission denied

Back to window 1:
trondmy@lade ~$ id
uid=520(trondmy) gid=100(users) groups=4(adm),20(dialout),24(cdrom),25(floppy),29(audio),30(dip),40(src),44(video),46(plugdev),100(users),106(lpadmin),110(scanner),112(admin),1000(trondmy)
trondmy@lade trondmy$ setfacl -m u:baz:r-x a.out
trondmy@lade trondmy$ ls -l /export/ext3/trondmy
total 12
-rwxr-xr--+ 1 trondmy users 7550 2006-07-13 08:38 a.out

Back to window 2:

baz@lade:/home/trondmy$ id
uid=1001(baz) gid=1001(baz) groups=1001(baz)
baz@lade:/tmp$ /export/ext3/trondmy/a.out
Could not create file foo
: File exists

Happy? The Linux ext3 filesystem does, IOW, allow you to set/clear
execution access via a POSIX acl. Just in case you're still sceptical,
I've also tested on a Linux XFS filesystem with exactly the same
results.

> bits to ignore the results.  

Linux has an additional test for whether at least one execute bit is set
when it decides whether or not the file is executable, but it can do
that because POSIX acls guarantee that at least one mode bit (the
'group' mode bit, which corresponds to the mask if acls are enabled on
the file as you saw above) will have the execute bit set.

NFSv4 acls do not offer the same guarantees, and so the Linux VFS
currently does have a problem with NFSv4 acls.

> cache entry need rarely be updated.

It still needs to take the trouble to set up an extra cache and to fill
that cache. If it is already caching ACCESS results, why is all that
extra bloat necessary? IOW: Apart from whining about how execute
permissions are ill-defined, you still haven't explained why checking
execute permissions is such an extra drag on the server when it already
needs to check read permissions (at minimum - in practice I believe most
clients will request all permissions when doing an ACCESS call).

IMO a better alternative would be to clarify the definition of execute
permissions in the NFSv4 acl model if it confuses you.

Trond

---

From: Mike Eisler <[email-masked]>
Date: Thu, 13 Jul 2006 20:49:14 -0700 (PDT)

--- Trond Myklebust <[email-masked]> wrote:

> Back to window 1:
> trondmy@lade ~$ id
> uid=520(trondmy) gid=100(users)
>
groups=4(adm),20(dialout),24(cdrom),25(floppy),29(audio),30(dip),40(src),44(video),46(plugdev),100(users),106(lpadmin),110(scanner),112(admin),1000(trondmy)
> Happy? The Linux ext3 filesystem does, IOW, allow you to set/clear

I'm unhappy that the setfacl to give baz execute access also gives
sets execute access to the group owner of the file. What you've
shown is that unless an x bit is set somewhere in the mode bits,
the file cannot be executed, regardless what the ACL
says.

> 'group' mode bit, which corresponds to the mask if acls are enabled

You keep saying that's what POSIX says. Others disagree.
In any case, because Linux ACLs have this artifact, they
ensure that there is an x bit in the mode if any ACE has
an x bit. What will Linux do when it encounters an NFS server
that doesn't have that artifact? How will this NFS server
divine that that Linux is assuming this artifact when it
gets an ACCESS call?

> > > Kiss both client and server performance goodbye if clients have
> to
> > > start
> It still needs to take the trouble to set up an extra cache and to

No, it's the same cache as the access cache. The existing
access cache wouldn't be filled from NFS server access results,
but instead would be filled from reading mode and acl. This
gives the client full control to interpret execute semantics
as it see fits.

> fill

Because in my proposal there are no execute ACCESS results
to cache. 

> execute permissions is such an extra drag on the server when it

Since the execute permissions are ill-defined, there's no point in
the server contributing to the client's buggy operating environment,
and creating conditions that compel server implementers to provide
kludges.

> already

I think it is nonsense for the client to override what ACCESS
returns. That's why ACCESS was added to NFS 14 years ago; to
solve the bugs that occurred when clients interpreted
permissions locally. Since clients are going to continue to this,
I believe that the least evil choice is for the server to stop
interpreting execute bits for ACCESS (interpretation for
purposes of allowing READ and OPEN would remain).

Even if all the client implementers could come to an agreement
about what ACCESS should return for all cases of mode and all types
of ACLs, eventually a new client would come along that would
break the convenant, and server implementors would be forced
to implement kludges again.

---

From: Trond Myklebust <[email-masked]>
Date: Fri, 14 Jul 2006 01:36:20 -0400

> the file cannot be executed, regardless what the ACL

No it doesn't set execute access to the group owner of the file:

trondmy@lade trondmy$ ls -l a.out
-rwxr-xr--+ 1 trondmy users 7550 2006-07-13 08:38 a.out
trondmy@lade trondmy$ getfacl a.out
# file: a.out
# owner: trondmy
# group: users
user::rwx
user:baz:r-x
group::r--
mask::r-x
other::r--

> You keep saying that's what POSIX says. Others disagree.

POSIX doesn't say anything: it never went beyond draft status. The Linux
implementation is based on draft 17 of POSIX 1003.1e, which has been
interpreted as described in

   http://marc.theaimsgroup.com/?l=ietf-nfsv4&m=112545912322026&w=2

> divine that that Linux is assuming this artifact when it

Linux feels that it needs to do this sort of hack to defend against
servers that fake up execute permissions on files. If a server tells the
user that all files are executable simply because that user has
administrator capabilities, then that has nasty security implications.

> gives the client full control to interpret execute semantics

It is _not_ the same cache. The client may, as you pointed out,
interpret the acl differently to the way the server does. This is
precisely why we've had a basic principle that the client defers to the
server when it comes to interpreting acls.

If you want to have a scheme like this, then your server is going to
have to fake up the stored acls depending on whether or not stuff like
root squashing, root read/write permission overriding etc is enabled so
that the client has the same information available to it that it had for
ACCESS.

On the other hand, faking up acl entries like that will _really_ screw
up operations that require a read-modify-write of those acls, since they
are likely to cause the entries that the server has faked up to be
committed to permanent storage.

> purposes of allowing READ and OPEN would remain).

<snip>

> to implement kludges again.

Client implementations will always want the ability to restrict the
permissions that the server grants: they have mount flags such as
'ro','noexec','nosuid' that are part of the administrator's arsenal of
security features for dealing with untrusted servers and/or users.

  Trond

---

From: Mike Eisler <[email-masked]>
Date: Fri, 14 Jul 2006 00:09:45 -0700 (PDT)

> trondmy@lade trondmy$ getfacl a.out

OK, then I'm unhappy it fakes up mode bits, because ls -l sure
looks to me like it says the file has execute permissions for
the group. But enough of the Linux ACL model; there's a whole
other thread for that.

> servers that fake up execute permissions on files. If a server tells

Which would be defensible except that Linux clients perform
the hack inconsistently: they hack execve() but not access(). So stuff
still breaks. Given that Linux clients do the hack inconsistently, 
and unlike UNIX, don't mask the inconsistency via the test command, 
even if servers didn't "fake up" execute permissions, there is still 
the problem that Linux and other clients have a peculiar 
interpretation of execute rights in ACLs. So if the mode bits are
zero, and there is a ACE that gives baz execute access, the NFSv[34]
server would correctly return ACCESS_EXECUTE, and the execve()
system call would fail. So no matter what, 
the NFS server implementer is screwed because he can't divine 
what the client wants.

> the

What might those be? 

> It is _not_ the same cache. The client may, as you pointed out,

The interface to the access cache takes a file handle, cred, and
access type as an argument and returns TRUE or FALSE. How is the
cache different? 

> that the client has the same information available to it that it had

To repeat an excerpt from my proposal:

"An NFS client that wants to see if an exec system call [will succeed]
can send an ACCESS with ACCESS4_READ set. If the file has a executable
bit set and not a read bit set, ACCESS can still return success."

Since this is consistent with OPEN and READ behavior, it's what
ACCESS should be doing anyway. NFSv4.1 would simply mandate it.

So for example, baz owns a file, with mode bits 0700,
root is getting "squashed", and it trys to
execute baz's file. execve() calls VOP_ACCESS(, VEXEC).
nfs4_access issues ACCESS (ACCESS4_READ). The NFS server returns zero
(no). 

If the mode bits were instead 0701, the NFS server would return
ACCESS4_READ (yes). The client then reads the mode to verify
that an execute bit is on, and then returns yes to the execve()
system call.

Another example would be a file owned by baz with mode 0000.
Root is not squashed. Root calls execve() -> VOP_ACCESS(,VEXEC) 
-> ACCESS (ACCESS4_READ). The server returns ACCESS4_READ to the
client, because root can read the file. The client then sees that no
execute
bit is set in the mode. The client returns no to the execve()
system call, and execve() fails.

The downsides are:

1. the non-privileged user that attempts to do:

   cp valuable_software /tmp/pirated_copy

where valuable_software has permissions 0111 and is on an
NFS server. The server is going to allow it, unless
the client steps in. I think it is up to the client to enforce software
copy protection if it wants to because nothing prevents the
non-privileged user from using a synthetic client to make the 
copy, because  nothing forces the synthetic client to call ACCESS 
before it calls OPEN.

2. The access cache implementation on the client is more complex, but
only the fill portion. 

> security features for dealing with untrusted servers and/or users.

Which is fine if the restrictions are applied consistently across
the system call interfaces. To argue on one hand that it is 
inelegant to restrict what access() returns to the "test" command 
and on the other that it is necessary to restrict what access() 
returns to execve() is not fine.

---

From: Trond Myklebust <[email-masked]>
Date: Fri, 14 Jul 2006 08:20:23 -0400

> the NFS server implementer is screwed because he can't divine 

I'm in the process of fixing that inconsistency. I don't see why a
current Linux bug requires a change in protocol.

> > administrator capabilities, then that has nasty security

For instance, there is no way to disable a buggy executable short of
removing the file.
I suppose you might be able to move it to some location that is
guaranteed never to be in anyone's path if you can control the clients
too, but that assumes that the client and server are under centralised
control.

> access type as an argument and returns TRUE or FALSE. How is the

The ACLs may be interpreted differently, hence you will get different
results depending on if you use ACCESS or GETATTR to retrieve the
information you require.

> returns to execve() is not fine.

You are trying to fix Linux bugs using protocol changes. Stop it!

Trond

---

From: "J. Bruce Fields" <[email-masked]>
Date: Fri, 14 Jul 2006 09:39:30 -0400

On Thu, Jul 13, 2006 at 08:49:14PM -0700, Mike Eisler wrote:
> sets execute access to the group owner of the file.

The bits reported as the group mode bits are the *maximum* over the
named user, named group, and group owner bits.  The group owner bits
themselves are actually unchanged.

This behavior is determined by two requirements:

	1. The mode bits should not represent the permissions as more
	restrictive than they really are.

	2. A chmod should never modify the ACL in any way that couldn't
	be undone by another chmod returning the mode bits to their
	previous state.

Requirement #1 is what forces the group mode bits to get the execute
bits, and requirement #2 is what prevents us from also adding the
execute bits to the group owner ace.

--b.

---

From: Mike Eisler <[email-masked]>
Date: Fri, 14 Jul 2006 08:22:06 -0700 (PDT)

> I'm in the process of fixing that inconsistency. I don't see why a

This is good news if it fix works out that way.

> current Linux bug requires a change in protocol.

This isn't the only change being made in partial reaction to
client or server bugs.

In NFSv4.1 sessions is an example what we 
can do in order to deal with clients that select an xid 
incorrectly or retry requests too frequently. The SSV is an example
of what we can do when clients don't implement machine
creds correctly. On the server side,
bound back channels and inverted RPCSEC_GSS contexts
are examples of what we can do to deal with servers that
don't implement callbacks correctly. 

Issue90 is only in partial reaction to client bugs.
The real issue that we've let go for too long is that 
end-to-end enforcement of execution permissions is impossible
to do in NFS because it is not a remote execution protocol
and it does not have Digital Rights Management.
 
> You are trying to fix Linux bugs using protocol changes. Stop it!

Actually most UNIX operating environments have the same
problem (execve() and access() return different answers), 
but as I noted, they mask it with the "test"
command looking for execute bits. When ACLs become
more prevalent, I expect "test" commands to stop masking
the issue on UNIX as well as Linux.

---

From: Mike Eisler <[email-masked]>
Date: Fri, 14 Jul 2006 08:22:06 -0700 (PDT)

---

From: Mike Eisler <[email-masked]>
Date: Mon, 31 Jul 2006 01:13:40 -0700 (PDT)

This isse has been resolved as "resolved-no-change".

No change is needed because NFSv4.[01] already have the
required apparatus to allowing a server to
not support ACCESS4_EXECUTE. The ACCESS operation has
a "supported" field which is the mask of
access types ACCESS can reliably report on. So
NFSv4.[01] can exclude ACCESS4_EXECUTE from "supported".