diff options
| -rw-r--r-- | Documentation/MANPAGE.md | 11 | ||||
| -rw-r--r-- | README.md | 3 | ||||
| -rw-r--r-- | cli_args.go | 6 | ||||
| -rw-r--r-- | internal/fusefrontend/args.go | 7 | ||||
| -rw-r--r-- | internal/fusefrontend/fs.go | 3 | ||||
| -rw-r--r-- | internal/fusefrontend_reverse/rfs.go | 9 | ||||
| -rw-r--r-- | main.go | 22 | ||||
| -rw-r--r-- | mount.go | 6 | 
8 files changed, 66 insertions, 1 deletions
| diff --git a/Documentation/MANPAGE.md b/Documentation/MANPAGE.md index 054fc66..119a3cb 100644 --- a/Documentation/MANPAGE.md +++ b/Documentation/MANPAGE.md @@ -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 @@ -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 diff --git a/cli_args.go b/cli_args.go index 5f4b328..bdce4ec 100644 --- a/cli_args.go +++ b/cli_args.go @@ -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. "+ diff --git a/internal/fusefrontend/args.go b/internal/fusefrontend/args.go index 5781db8..37f4463 100644 --- a/internal/fusefrontend/args.go +++ b/internal/fusefrontend/args.go @@ -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. diff --git a/internal/fusefrontend/fs.go b/internal/fusefrontend/fs.go index c589302..16707d6 100644 --- a/internal/fusefrontend/fs.go +++ b/internal/fusefrontend/fs.go @@ -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  } diff --git a/internal/fusefrontend_reverse/rfs.go b/internal/fusefrontend_reverse/rfs.go index 63384ac..3c84e15 100644 --- a/internal/fusefrontend_reverse/rfs.go +++ b/internal/fusefrontend_reverse/rfs.go @@ -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  } @@ -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) @@ -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 { | 
