utime disallowed by user set with force_owner when not given direct FD on file
charles-dyfis-net opened this issue · 3 comments
With a file stored on gocryptfs 2.1, when force_owner
is used to present the mount point as owned by a different (non-root) user, a process running as that non-root user can successfully run the following Python code to copy a file while preserving its timestamp:
#!/usr/bin/env python3
import os, sys, shutil
source_name, dest_name = sys.argv[1:3]
src_stat = os.lstat(source_name)
with open(source_name, 'rb') as infile:
with open(dest_name, 'wb') as outfile:
shutil.copyfileobj(infile, outfile)
outfile.flush()
os.utime(outfile.fileno(), (src_stat.st_atime, src_stat.st_mtime))
...but cannot run the following, which differs only that it passes the utimensat
syscall a path to the target file as opposed to an already-opened file handle:
#!/usr/bin/env python3
import os, sys, shutil
source_name, dest_name = sys.argv[1:3]
src_stat = os.lstat(source_name)
with open(source_name, 'rb') as infile:
with open(dest_name, 'wb') as outfile:
shutil.copyfileobj(infile, outfile)
os.utime(source_name, (src_stat.st_atime, src_stat.st_mtime))
...which receives a PermissionError: [Errno 1] Operation not permitted
This issue did not exist in gocryptfs 1.7 -- and it impacts far more applications than just the reproducer above (for example, rsync
passes utimensat
a relative path to open and is subject to this bug).
In the environment where this is reproduced, the backing store for gocryptfs is owned by a special-purpose user that does nothing other than own this directory and run gocryptfs processes; the mount options include -force_owner
with the uid and gid of the user running the above code (which is different from the account running gocryptfs itself), and -allow_other
.
After inspecting with -fusedebug
, it looks like force_owner
behavior has changed between 1.7 and 2.1.
With gocryptfs 1.7, the response to a CREATE or a LOOKUP has the force_owner-specified UID.
With gocryptfs 2.1, the response to a CREATE or a LOOKUP has the underlying UID, ignoring the value specified for force_owner
.
#610 fixes this issue, confirming that it is in fact caused by force_owner
not being honored for LOOKUP and CREATE.