summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCharles Duffy2017-05-30 16:01:06 -0500
committerrfjakob2017-06-01 00:26:17 +0200
commitcf1ded5236157e2f9ec06eeea26023b67b40f16d (patch)
tree48c9926efd8c10a76b6f28943397f8b1ae5cc3da
parentfc2a5f5ab0149d48b5d45a9af96799b07d802ae6 (diff)
Implement force_owner option to display ownership as a specific user.
-rw-r--r--Documentation/MANPAGE.md11
-rw-r--r--README.md3
-rw-r--r--cli_args.go6
-rw-r--r--internal/fusefrontend/args.go7
-rw-r--r--internal/fusefrontend/fs.go3
-rw-r--r--internal/fusefrontend_reverse/rfs.go9
-rw-r--r--main.go22
-rw-r--r--mount.go6
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
diff --git a/README.md b/README.md
index ed1c1fc..7902287 100644
--- a/README.md
+++ b/README.md
@@ -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
}
diff --git a/main.go b/main.go
index 8dada52..9b0e31b 100644
--- a/main.go
+++ b/main.go
@@ -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)
diff --git a/mount.go b/mount.go
index c10f90b..c2d1f74 100644
--- a/mount.go
+++ b/mount.go
@@ -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 {