Hello,
I have encountered a problem with access(dir, W_OK) calls and world writable directories on NFS which are not owner by the calling user. The call returns EACCES, even though the user can create files in the directory. I use NFSv3, but this may also impact other NFS versions.
Test case, on an NFSv3 mounted directory is:
1. Create world writable directory as root: sudo mkdir -m 777 testdir
2. Test that normal user can create a file: rm -f testdir/foo && touch testdir/foo
3. Test for writability with access: strace -o log test -w testdir && echo PASS || echo FAIL
4. Checking the strace log shows that access("testdir", W_OK) fails with EACCES
I am using the linux-intel kernel 4.19.13, but I traced this to commit ecbb903c56745 in linux master (NFS: Be more careful about mapping file permissions), that went into kernel 4.13. The test works fine in earlier kernels. The problem does not always show up on directories owned by the calling user, but it may be due to attribute caching or similar.
I am not very NFS savvy but as far as I understand the commit above changed the tests in nfs_access_calc_mask() from "any bit in mask is set" to "all bits in mask are set". The mask for writable directories is ACCESS_MODIFY, ACCESS_EXTEND, ACCESS_DELETE but from what I see in a network capture the response from the NFS server is lacking the ACCESS_DELETE bit.
The attached patch reverts the tests in nfs_access_calc_mask() to "any bit in mask is set", which makes the test case work again, but I am not sure it is entirely correct to change all the tests, or if it would be more appropriate to drop ACCESS_DELETE from the NFS_DIR_MAY_WRITE mask. The patch applies cleanly on current linux master.
I checked the NFSv3 and NFSv4 RFCs, but it is not clear to me if ACCESS_DELETE is for the directory itself or its children. In any case my NFS server is omitting ACCESS_DELETE from the response (it is a Synology running Linux kernel 3.10.105).
Sincerely,
Diego
--
Diego Santa Cruz, PhD
Technology Architect
T +41 21 341 15 50
[email protected]
spinetix.com
On Tue, 2019-04-09 at 09:26 +0000, Diego Santa Cruz wrote:
> Hello,
>
> I have encountered a problem with access(dir, W_OK) calls and world
> writable directories on NFS which are not owner by the calling user.
> The call returns EACCES, even though the user can create files in the
> directory. I use NFSv3, but this may also impact other NFS versions.
>
> Test case, on an NFSv3 mounted directory is:
> 1. Create world writable directory as root: sudo mkdir -m 777 testdir
> 2. Test that normal user can create a file: rm -f testdir/foo &&
> touch testdir/foo
> 3. Test for writability with access: strace -o log test -w testdir &&
> echo PASS || echo FAIL
> 4. Checking the strace log shows that access("testdir", W_OK) fails
> with EACCES
>
> I am using the linux-intel kernel 4.19.13, but I traced this to
> commit ecbb903c56745 in linux master (NFS: Be more careful about
> mapping file permissions), that went into kernel 4.13. The test works
> fine in earlier kernels. The problem does not always show up on
> directories owned by the calling user, but it may be due to attribute
> caching or similar.
>
> I am not very NFS savvy but as far as I understand the commit above
> changed the tests in nfs_access_calc_mask() from "any bit in mask is
> set" to "all bits in mask are set". The mask for writable directories
> is ACCESS_MODIFY, ACCESS_EXTEND, ACCESS_DELETE but from what I see in
> a network capture the response from the NFS server is lacking the
> ACCESS_DELETE bit.
>
> The attached patch reverts the tests in nfs_access_calc_mask() to
> "any bit in mask is set", which makes the test case work again, but I
> am not sure it is entirely correct to change all the tests, or if it
> would be more appropriate to drop ACCESS_DELETE from the
> NFS_DIR_MAY_WRITE mask. The patch applies cleanly on current linux
> master.
>
> I checked the NFSv3 and NFSv4 RFCs, but it is not clear to me if
> ACCESS_DELETE is for the directory itself or its children. In any
> case my NFS server is omitting ACCESS_DELETE from the response (it is
> a Synology running Linux kernel 3.10.105).
>
The ACCESS_DELETE bit applies _only_ to directories, so if it is
missing in the reply from your server for a directory with POSIX 777
permissions, then you should talk to the server vendor.
As far as I can see, the current code is correct. If you do not have
the ACCESS_DELETE bit set on your directory, then the server is telling
the client that your process does not have full write permissions to
that directory in accordance with the POSIX spec.
Cheers
Trond
--
Trond Myklebust
Linux NFS client maintainer, Hammerspace
[email protected]
> -----Original Message-----
> From: Trond Myklebust <[email protected]>
> Sent: 09 April 2019 14:54
> To: [email protected]; Diego Santa Cruz
> <[email protected]>
> Cc: [email protected]
> Subject: Re: Write access check not correct on world writable directories
> (regression)
>
> On Tue, 2019-04-09 at 09:26 +0000, Diego Santa Cruz wrote:
> > Hello,
> >
> > I have encountered a problem with access(dir, W_OK) calls and world
> > writable directories on NFS which are not owner by the calling user.
> > The call returns EACCES, even though the user can create files in the
> > directory. I use NFSv3, but this may also impact other NFS versions.
> >
> > Test case, on an NFSv3 mounted directory is:
> > 1. Create world writable directory as root: sudo mkdir -m 777 testdir
> > 2. Test that normal user can create a file: rm -f testdir/foo &&
> > touch testdir/foo
> > 3. Test for writability with access: strace -o log test -w testdir &&
> > echo PASS || echo FAIL
> > 4. Checking the strace log shows that access("testdir", W_OK) fails
> > with EACCES
> >
> > I am using the linux-intel kernel 4.19.13, but I traced this to
> > commit ecbb903c56745 in linux master (NFS: Be more careful about
> > mapping file permissions), that went into kernel 4.13. The test works
> > fine in earlier kernels. The problem does not always show up on
> > directories owned by the calling user, but it may be due to attribute
> > caching or similar.
> >
> > I am not very NFS savvy but as far as I understand the commit above
> > changed the tests in nfs_access_calc_mask() from "any bit in mask is
> > set" to "all bits in mask are set". The mask for writable directories
> > is ACCESS_MODIFY, ACCESS_EXTEND, ACCESS_DELETE but from what I see in
> > a network capture the response from the NFS server is lacking the
> > ACCESS_DELETE bit.
> >
> > The attached patch reverts the tests in nfs_access_calc_mask() to
> > "any bit in mask is set", which makes the test case work again, but I
> > am not sure it is entirely correct to change all the tests, or if it
> > would be more appropriate to drop ACCESS_DELETE from the
> > NFS_DIR_MAY_WRITE mask. The patch applies cleanly on current linux
> > master.
> >
> > I checked the NFSv3 and NFSv4 RFCs, but it is not clear to me if
> > ACCESS_DELETE is for the directory itself or its children. In any
> > case my NFS server is omitting ACCESS_DELETE from the response (it is
> > a Synology running Linux kernel 3.10.105).
> >
>
> The ACCESS_DELETE bit applies _only_ to directories, so if it is
> missing in the reply from your server for a directory with POSIX 777
> permissions, then you should talk to the server vendor.
>
> As far as I can see, the current code is correct. If you do not have
> the ACCESS_DELETE bit set on your directory, then the server is telling
> the client that your process does not have full write permissions to
> that directory in accordance with the POSIX spec.
>
Sorry for the noise. Indeed it appears that the behavior is specific to my NFS vendor, I wrongly assumed that being a Linux kernel they did not tweak this kind of stuff in the NFS server... I repeated the test with CentOS 7 and CentOS 5 servers and they behave correctly, the ACCESS_DELETE is apparently set based on the directory being writable by the caller ID at the server.
But it remains that the commit does break compatibility with existing products (and fairly popular I believe). I'll report the issue to the NFS vendor, but it is always problematic when compatibility breaks like this, it may take a long time to convince the vendor that there is a bug on their side.
For completeness, I think my initial understanding and description was not entirely correct, as it is not really the fact that the directory is world readable that is a problem, but that the caller ID has no write permissions to the parent directory. In that case the directory cannot be removed by the caller because he has no write permissions to the parent. The test case would thus be the following
# Change directory to a NFS mount
# Create parent directory as root
sudo mkdir -m 755 parentdir
# Create a world readable child directory
sudo mkdir -m 777 parentdir/childdir
# Test that normal user can create a file
rm -f parentdir/childdir/foo && touch parentdir/childdir/foo
# Test for writability with access
strace -o parentdir/childdir/log test -w parentdir/childdir \
&& echo SUCCESS || echo FAIL
# In the strace log
# access("parentdir/childdir", W_OK) = -1 EACCES (Permission denied)
As far as I understand, the problematic NFS server sets ACCESS_DELETE if the directory entry itself can be removed, not if entries within the directory can be removed (repeating the test with perentdir having mode 777 succeeds and the network capture shows that ACCESS_DELETE is present in the ACCESS response).
Best,
Diego
> Cheers
> Trond
> --
> Trond Myklebust
> Linux NFS client maintainer, Hammerspace
> [email protected]
>