main: accept -dev, -nodev, -suid, -nosuid, -exec, -noexec
When mounted via /etc/fstab like this, /a /b fuse.gocryptfs default 0 0 we always get extra options passed. As reported by @mahkoh at https://github.com/rfjakob/gocryptfs/pull/233 : mount passes `-o noexec` if `-o user` is set and `-o exec` is not set. If both `-o user` and `-o exec` are set, it passes `-o exec`. Make these options work, and in addtion, also make -suid and -rw work the same way. Reported-by: @mahkoh
This commit is contained in:
parent
e29a81efc3
commit
53d6a9999d
22
cli_args.go
22
cli_args.go
@ -20,9 +20,11 @@ import (
|
|||||||
type argContainer struct {
|
type argContainer struct {
|
||||||
debug, init, zerokey, fusedebug, openssl, passwd, fg, version,
|
debug, init, zerokey, fusedebug, openssl, passwd, fg, version,
|
||||||
plaintextnames, quiet, nosyslog, wpanic,
|
plaintextnames, quiet, nosyslog, wpanic,
|
||||||
longnames, allow_other, ro, reverse, aessiv, nonempty, raw64,
|
longnames, allow_other, reverse, aessiv, nonempty, raw64,
|
||||||
noprealloc, speed, hkdf, serialize_reads, forcedecode, hh, info,
|
noprealloc, speed, hkdf, serialize_reads, forcedecode, hh, info,
|
||||||
sharedstorage, devrandom, fsck bool
|
sharedstorage, devrandom, fsck bool
|
||||||
|
// Mount options with opposites
|
||||||
|
dev, nodev, suid, nosuid, exec, noexec, rw, ro bool
|
||||||
masterkey, mountpoint, cipherdir, cpuprofile, extpass,
|
masterkey, mountpoint, cipherdir, cpuprofile, extpass,
|
||||||
memprofile, ko, passfile, ctlsock, fsname, force_owner, trace string
|
memprofile, ko, passfile, ctlsock, fsname, force_owner, trace string
|
||||||
// Configuration file name override
|
// Configuration file name override
|
||||||
@ -121,7 +123,6 @@ func parseCliOpts() (args argContainer) {
|
|||||||
flagSet.BoolVar(&args.longnames, "longnames", true, "Store names longer than 176 bytes in extra files")
|
flagSet.BoolVar(&args.longnames, "longnames", true, "Store names longer than 176 bytes in extra files")
|
||||||
flagSet.BoolVar(&args.allow_other, "allow_other", false, "Allow other users to access the filesystem. "+
|
flagSet.BoolVar(&args.allow_other, "allow_other", false, "Allow other users to access the filesystem. "+
|
||||||
"Only works if user_allow_other is set in /etc/fuse.conf.")
|
"Only works if user_allow_other is set in /etc/fuse.conf.")
|
||||||
flagSet.BoolVar(&args.ro, "ro", false, "Mount the filesystem read-only")
|
|
||||||
flagSet.BoolVar(&args.reverse, "reverse", false, "Reverse mode")
|
flagSet.BoolVar(&args.reverse, "reverse", false, "Reverse mode")
|
||||||
flagSet.BoolVar(&args.aessiv, "aessiv", false, "AES-SIV encryption")
|
flagSet.BoolVar(&args.aessiv, "aessiv", false, "AES-SIV encryption")
|
||||||
flagSet.BoolVar(&args.nonempty, "nonempty", false, "Allow mounting over non-empty directories")
|
flagSet.BoolVar(&args.nonempty, "nonempty", false, "Allow mounting over non-empty directories")
|
||||||
@ -137,6 +138,17 @@ func parseCliOpts() (args argContainer) {
|
|||||||
flagSet.BoolVar(&args.sharedstorage, "sharedstorage", false, "Make concurrent access to a shared CIPHERDIR safer")
|
flagSet.BoolVar(&args.sharedstorage, "sharedstorage", false, "Make concurrent access to a shared CIPHERDIR safer")
|
||||||
flagSet.BoolVar(&args.devrandom, "devrandom", false, "Use /dev/random for generating master key")
|
flagSet.BoolVar(&args.devrandom, "devrandom", false, "Use /dev/random for generating master key")
|
||||||
flagSet.BoolVar(&args.fsck, "fsck", false, "Run a filesystem check on CIPHERDIR")
|
flagSet.BoolVar(&args.fsck, "fsck", false, "Run a filesystem check on CIPHERDIR")
|
||||||
|
|
||||||
|
// Mount options with opposites
|
||||||
|
flagSet.BoolVar(&args.dev, "dev", false, "Allow device files")
|
||||||
|
flagSet.BoolVar(&args.nodev, "nodev", false, "Deny device files")
|
||||||
|
flagSet.BoolVar(&args.suid, "suid", false, "Allow suid binaries")
|
||||||
|
flagSet.BoolVar(&args.nosuid, "nosuid", false, "Deny suid binaries")
|
||||||
|
flagSet.BoolVar(&args.exec, "exec", false, "Allow executables")
|
||||||
|
flagSet.BoolVar(&args.noexec, "noexec", false, "Deny executables")
|
||||||
|
flagSet.BoolVar(&args.rw, "rw", false, "Mount the filesystem read-write")
|
||||||
|
flagSet.BoolVar(&args.ro, "ro", false, "Mount the filesystem read-only")
|
||||||
|
|
||||||
flagSet.StringVar(&args.masterkey, "masterkey", "", "Mount with explicit master key")
|
flagSet.StringVar(&args.masterkey, "masterkey", "", "Mount with explicit master key")
|
||||||
flagSet.StringVar(&args.cpuprofile, "cpuprofile", "", "Write cpu profile to specified file")
|
flagSet.StringVar(&args.cpuprofile, "cpuprofile", "", "Write cpu profile to specified file")
|
||||||
flagSet.StringVar(&args.memprofile, "memprofile", "", "Write memory profile to specified file")
|
flagSet.StringVar(&args.memprofile, "memprofile", "", "Write memory profile to specified file")
|
||||||
@ -152,12 +164,6 @@ func parseCliOpts() (args argContainer) {
|
|||||||
"successful mount - used internally for daemonization")
|
"successful mount - used internally for daemonization")
|
||||||
flagSet.IntVar(&args.scryptn, "scryptn", configfile.ScryptDefaultLogN, "scrypt cost parameter logN. Possible values: 10-28. "+
|
flagSet.IntVar(&args.scryptn, "scryptn", configfile.ScryptDefaultLogN, "scrypt cost parameter logN. Possible values: 10-28. "+
|
||||||
"A lower value speeds up mounting and reduces its memory needs, but makes the password susceptible to brute-force attacks")
|
"A lower value speeds up mounting and reduces its memory needs, but makes the password susceptible to brute-force attacks")
|
||||||
// Ignored otions
|
|
||||||
var dummyBool bool
|
|
||||||
ignoreText := "(ignored for compatibility)"
|
|
||||||
flagSet.BoolVar(&dummyBool, "rw", false, ignoreText)
|
|
||||||
flagSet.BoolVar(&dummyBool, "nosuid", false, ignoreText)
|
|
||||||
flagSet.BoolVar(&dummyBool, "nodev", false, ignoreText)
|
|
||||||
var dummyString string
|
var dummyString string
|
||||||
flagSet.StringVar(&dummyString, "o", "", "For compatibility with mount(1), options can be also passed as a comma-separated list to -o on the end.")
|
flagSet.StringVar(&dummyString, "o", "", "For compatibility with mount(1), options can be also passed as a comma-separated list to -o on the end.")
|
||||||
// Actual parsing
|
// Actual parsing
|
||||||
|
@ -37,7 +37,7 @@ func forkChild() int {
|
|||||||
exitOnUsr1()
|
exitOnUsr1()
|
||||||
err := c.Start()
|
err := c.Start()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tlog.Fatal.Printf("forkChild: starting %s failed: %v\n", name, err)
|
tlog.Fatal.Printf("forkChild: starting %s failed: %v", name, err)
|
||||||
return exitcodes.ForkChild
|
return exitcodes.ForkChild
|
||||||
}
|
}
|
||||||
err = c.Wait()
|
err = c.Wait()
|
||||||
@ -47,7 +47,7 @@ func forkChild() int {
|
|||||||
os.Exit(waitstat.ExitStatus())
|
os.Exit(waitstat.ExitStatus())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tlog.Fatal.Printf("forkChild: wait returned an unknown error: %v\n", err)
|
tlog.Fatal.Printf("forkChild: wait returned an unknown error: %v", err)
|
||||||
return exitcodes.ForkChild
|
return exitcodes.ForkChild
|
||||||
}
|
}
|
||||||
// The child exited with 0 - let's do the same.
|
// The child exited with 0 - let's do the same.
|
||||||
|
20
mount.go
20
mount.go
@ -314,17 +314,33 @@ func initGoFuse(fs pathfs.FileSystem, args *argContainer) *fuse.Server {
|
|||||||
if args.reverse {
|
if args.reverse {
|
||||||
mOpts.Name += "-reverse"
|
mOpts.Name += "-reverse"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add a volume name if running osxfuse. Otherwise the Finder will show it as
|
// Add a volume name if running osxfuse. Otherwise the Finder will show it as
|
||||||
// something like "osxfuse Volume 0 (gocryptfs)".
|
// something like "osxfuse Volume 0 (gocryptfs)".
|
||||||
if runtime.GOOS == "darwin" {
|
if runtime.GOOS == "darwin" {
|
||||||
mOpts.Options = append(mOpts.Options, "volname="+path.Base(args.mountpoint))
|
mOpts.Options = append(mOpts.Options, "volname="+path.Base(args.mountpoint))
|
||||||
}
|
}
|
||||||
|
|
||||||
// The kernel enforces read-only operation, we just have to pass "ro".
|
// The kernel enforces read-only operation, we just have to pass "ro".
|
||||||
// Reverse mounts are always read-only.
|
// Reverse mounts are always read-only.
|
||||||
if args.ro || args.reverse {
|
if args.ro || args.reverse {
|
||||||
mOpts.Options = append(mOpts.Options, "ro")
|
mOpts.Options = append(mOpts.Options, "ro")
|
||||||
|
} else if args.rw {
|
||||||
|
mOpts.Options = append(mOpts.Options, "rw")
|
||||||
|
}
|
||||||
|
// If both "nosuid" and "suid" were passed, the safer option wins.
|
||||||
|
if args.nosuid {
|
||||||
|
mOpts.Options = append(mOpts.Options, "nosuid")
|
||||||
|
} else if args.suid {
|
||||||
|
mOpts.Options = append(mOpts.Options, "suid")
|
||||||
|
}
|
||||||
|
if args.nodev {
|
||||||
|
mOpts.Options = append(mOpts.Options, "nodev")
|
||||||
|
} else if args.dev {
|
||||||
|
mOpts.Options = append(mOpts.Options, "dev")
|
||||||
|
}
|
||||||
|
if args.noexec {
|
||||||
|
mOpts.Options = append(mOpts.Options, "noexec")
|
||||||
|
} else if args.exec {
|
||||||
|
mOpts.Options = append(mOpts.Options, "exec")
|
||||||
}
|
}
|
||||||
// Add additional mount options (if any) after the stock ones, so the user has
|
// Add additional mount options (if any) after the stock ones, so the user has
|
||||||
// a chance to override them.
|
// a chance to override them.
|
||||||
|
@ -449,6 +449,30 @@ func TestMultipleOperationFlags(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNoexec(t *testing.T) {
|
||||||
|
dir := test_helpers.InitFS(t)
|
||||||
|
mnt := dir + ".mnt"
|
||||||
|
err := os.Mkdir(mnt, 0700)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
test_helpers.MountOrFatal(t, dir, mnt, "-extpass=echo test", "-noexec")
|
||||||
|
defer test_helpers.UnmountPanic(mnt)
|
||||||
|
sh := mnt + "/x.sh"
|
||||||
|
content := `#!/bin/bash
|
||||||
|
echo hello
|
||||||
|
`
|
||||||
|
err = ioutil.WriteFile(sh, []byte(content), 0755)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
err = exec.Command(sh).Run()
|
||||||
|
exitCode := test_helpers.ExtractCmdExitCode(err)
|
||||||
|
if exitCode != int(syscall.EACCES) {
|
||||||
|
t.Errorf("got exitcode %d instead of EPERM (%d)", exitCode, syscall.EPERM)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Test that a missing argument to "-o" triggers exit code 1.
|
// Test that a missing argument to "-o" triggers exit code 1.
|
||||||
// See also cli_args_test.go for comprehensive tests of "-o" parsing.
|
// See also cli_args_test.go for comprehensive tests of "-o" parsing.
|
||||||
func TestMissingOArg(t *testing.T) {
|
func TestMissingOArg(t *testing.T) {
|
||||||
|
@ -433,9 +433,14 @@ func ExtractCmdExitCode(err error) int {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
// OMG this is convoluted
|
// OMG this is convoluted
|
||||||
err2 := err.(*exec.ExitError)
|
if err2, ok := err.(*exec.ExitError); ok {
|
||||||
code := err2.Sys().(syscall.WaitStatus).ExitStatus()
|
return err2.Sys().(syscall.WaitStatus).ExitStatus()
|
||||||
return code
|
}
|
||||||
|
if err2, ok := err.(*os.PathError); ok {
|
||||||
|
return int(err2.Err.(syscall.Errno))
|
||||||
|
}
|
||||||
|
log.Panicf("could not decode error %#v", err)
|
||||||
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListFds lists our open file descriptors.
|
// ListFds lists our open file descriptors.
|
||||||
|
Loading…
Reference in New Issue
Block a user