Implement force_owner option to display ownership as a specific user.

This commit is contained in:
Charles Duffy 2017-05-30 16:01:06 -05:00 committed by rfjakob
parent fc2a5f5ab0
commit cf1ded5236
8 changed files with 66 additions and 1 deletions

View File

@ -78,6 +78,17 @@ that uses built-in Go crypto.
Setting this option forces the filesystem to read-only and noexec.
#### -force_owner string
If given a string of the form "uid:gid" (where both "uid" and "gid" are
substituted with positive integers), presents all files as owned by the given
uid and gid, regardless of their actual ownership. Implies "allow_other".
This is rarely desired behavior: One should *usually* run gocryptfs as the
account which owns the backing-store files, which should *usually* be one and
the same with the account intended to access the decrypted content. An example
of a case where this may be useful is a situation where content is stored on a
filesystem that doesn't properly support UNIX ownership and permissions.
#### -fsname string
Override the filesystem name (first column in df -T). Can also be
passed as "-o fsname=" and is equivalent to libfuse's option of the

View File

@ -144,6 +144,9 @@ Changelog
---------
v1.4 (not yet released)
* Add `force_owner` option to allow files to be presented as owned by a
different user or group from the user running gocryptfs. Please see caveats
and guidance in the man page before using this functionality.
* Increase open file limit to 4096 ([#82](https://github.com/rfjakob/gocryptfs/issues/82)).
* Implement path decryption via ctlsock ([#84](https://github.com/rfjakob/gocryptfs/issues/84)).
Previously, decryption was only implemented for reverse mode. Now both

View File

@ -13,6 +13,7 @@ import (
"github.com/rfjakob/gocryptfs/internal/prefer_openssl"
"github.com/rfjakob/gocryptfs/internal/stupidgcm"
"github.com/rfjakob/gocryptfs/internal/tlog"
"github.com/hanwen/go-fuse/fuse"
)
// argContainer stores the parsed CLI options and arguments
@ -22,7 +23,7 @@ type argContainer struct {
longnames, allow_other, ro, reverse, aessiv, nonempty, raw64,
noprealloc, speed, hkdf, serialize_reads, forcedecode, hh, info bool
masterkey, mountpoint, cipherdir, cpuprofile, extpass,
memprofile, ko, passfile, ctlsock, fsname string
memprofile, ko, passfile, ctlsock, fsname, force_owner string
// Configuration file name override
config string
notifypid, scryptn int
@ -31,6 +32,8 @@ type argContainer struct {
_configCustom bool
// _ctlsockFd stores the control socket file descriptor (ctlsock stores the path)
_ctlsockFd net.Listener
// _forceOwner is, if non-nil, a parsed, validated Owner (as opposed to the string above)
_forceOwner *fuse.Owner
}
var flagSet *flag.FlagSet
@ -136,6 +139,7 @@ func parseCliOpts() (args argContainer) {
flagSet.StringVar(&args.ko, "ko", "", "Pass additional options directly to the kernel, comma-separated list")
flagSet.StringVar(&args.ctlsock, "ctlsock", "", "Create control socket at specified path")
flagSet.StringVar(&args.fsname, "fsname", "", "Override the filesystem name")
flagSet.StringVar(&args.force_owner, "force_owner", "", "uid:gid pair to coerce ownership")
flagSet.IntVar(&args.notifypid, "notifypid", 0, "Send USR1 to the specified process after "+
"successful mount - used internally for daemonization")
flagSet.IntVar(&args.scryptn, "scryptn", configfile.ScryptDefaultLogN, "scrypt cost parameter logN. Possible values: 10-28. "+

View File

@ -1,6 +1,7 @@
package fusefrontend
import (
"github.com/hanwen/go-fuse/fuse"
"github.com/rfjakob/gocryptfs/internal/cryptocore"
)
@ -16,6 +17,12 @@ type Args struct {
// Should we chown a file after it has been created?
// This only makes sense if (1) allow_other is set and (2) we run as root.
PreserveOwner bool
// Should we force ownership to be presented with a given user and group?
// This only makes sense if allow_other is set. In *most* cases, it also
// only makes sense with PreserveOwner set, but can also make sense without
// PreserveOwner if the underlying filesystem acting as backing store
// enforces ownership itself.
ForceOwner *fuse.Owner
// ConfigCustom is true when the user select a non-default config file
// location. If it is false, reverse mode maps ".gocryptfs.reverse.conf"
// to "gocryptfs.conf" in the plaintext dir.

View File

@ -77,6 +77,9 @@ func (fs *FS) GetAttr(name string, context *fuse.Context) (*fuse.Attr, fuse.Stat
target, _ := fs.Readlink(name, context)
a.Size = uint64(len(target))
}
if fs.args.ForceOwner != nil {
a.Owner = *fs.args.ForceOwner
}
return a, status
}

View File

@ -115,6 +115,9 @@ func (rfs *ReverseFS) GetAttr(relPath string, context *fuse.Context) (*fuse.Attr
}
var a fuse.Attr
a.FromStat(&st)
if rfs.args.ForceOwner != nil {
a.Owner = *rfs.args.ForceOwner
}
return &a, fuse.OK
}
// Handle virtual files (gocryptfs.diriv, *.name)
@ -136,6 +139,9 @@ func (rfs *ReverseFS) GetAttr(relPath string, context *fuse.Context) (*fuse.Attr
}
var a fuse.Attr
status = f.GetAttr(&a)
if rfs.args.ForceOwner != nil {
a.Owner = *rfs.args.ForceOwner
}
return &a, status
}
// Decrypt path to "plaintext relative path"
@ -177,6 +183,9 @@ func (rfs *ReverseFS) GetAttr(relPath string, context *fuse.Context) (*fuse.Attr
a.Size = uint64(len(linkTarget))
}
if rfs.args.ForceOwner != nil {
a.Owner = *rfs.args.ForceOwner
}
return &a, fuse.OK
}

22
main.go
View File

@ -8,6 +8,7 @@ import (
"runtime"
"runtime/pprof"
"strconv"
"strings"
"time"
"github.com/rfjakob/gocryptfs/internal/configfile"
@ -17,6 +18,7 @@ import (
"github.com/rfjakob/gocryptfs/internal/speed"
"github.com/rfjakob/gocryptfs/internal/stupidgcm"
"github.com/rfjakob/gocryptfs/internal/tlog"
"github.com/hanwen/go-fuse/fuse"
)
// GitVersion is the gocryptfs version according to git, set by build.bash
@ -190,6 +192,26 @@ func main() {
pprof.StartCPUProfile(f)
defer pprof.StopCPUProfile()
}
// "-force_owner"
if args.force_owner != "" {
var uidNum, gidNum int64
ownerPieces := strings.SplitN(args.force_owner, ":", 2)
if len(ownerPieces) != 2 {
tlog.Fatal.Printf("force_owner must be in form UID:GID")
os.Exit(exitcodes.Usage)
}
uidNum, err = strconv.ParseInt(ownerPieces[0], 0, 32)
if err != nil || uidNum < 0 {
tlog.Fatal.Printf("force_owner: Unable to parse UID %v as positive integer", ownerPieces[0])
os.Exit(exitcodes.Usage)
}
gidNum, err = strconv.ParseInt(ownerPieces[1], 0, 32)
if err != nil || gidNum < 0 {
tlog.Fatal.Printf("force_owner: Unable to parse GID %v as positive integer", ownerPieces[1])
os.Exit(exitcodes.Usage)
}
args._forceOwner = &fuse.Owner{Uid: uint32(uidNum), Gid: uint32(gidNum)}
}
// "-memprofile"
if args.memprofile != "" {
tlog.Info.Printf("Writing mem profile to %s", args.memprofile)

View File

@ -217,6 +217,11 @@ func initFuseFrontend(key []byte, args *argContainer, confFile *configfile.ConfF
if args.aessiv {
cryptoBackend = cryptocore.BackendAESSIV
}
// forceOwner implies allow_other, as documented.
// Set this early, so args.allow_other can be relied on below this point.
if args._forceOwner != nil {
args.allow_other = true
}
frontendArgs := fusefrontend.Args{
Cipherdir: args.cipherdir,
Masterkey: key,
@ -229,6 +234,7 @@ func initFuseFrontend(key []byte, args *argContainer, confFile *configfile.ConfF
HKDF: args.hkdf,
SerializeReads: args.serialize_reads,
ForceDecode: args.forcedecode,
ForceOwner: args._forceOwner,
}
// confFile is nil when "-zerokey" or "-masterkey" was used
if confFile != nil {