diff --git a/cli_args.go b/cli_args.go index 6253c14..76876d6 100644 --- a/cli_args.go +++ b/cli_args.go @@ -20,9 +20,11 @@ import ( type argContainer struct { debug, init, zerokey, fusedebug, openssl, passwd, fg, version, 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, sharedstorage, devrandom, fsck bool + // Mount options with opposites + dev, nodev, suid, nosuid, exec, noexec, rw, ro bool masterkey, mountpoint, cipherdir, cpuprofile, extpass, memprofile, ko, passfile, ctlsock, fsname, force_owner, trace string // 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.allow_other, "allow_other", false, "Allow other users to access the filesystem. "+ "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.aessiv, "aessiv", false, "AES-SIV encryption") 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.devrandom, "devrandom", false, "Use /dev/random for generating master key") 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.cpuprofile, "cpuprofile", "", "Write cpu 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") 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") - // 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 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 diff --git a/daemonize.go b/daemonize.go index 03c4fc4..abb6d08 100644 --- a/daemonize.go +++ b/daemonize.go @@ -37,7 +37,7 @@ func forkChild() int { exitOnUsr1() err := c.Start() 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 } err = c.Wait() @@ -47,7 +47,7 @@ func forkChild() int { 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 } // The child exited with 0 - let's do the same. diff --git a/mount.go b/mount.go index 54ceedc..cbe77b4 100644 --- a/mount.go +++ b/mount.go @@ -314,17 +314,33 @@ func initGoFuse(fs pathfs.FileSystem, args *argContainer) *fuse.Server { if args.reverse { mOpts.Name += "-reverse" } - // Add a volume name if running osxfuse. Otherwise the Finder will show it as // something like "osxfuse Volume 0 (gocryptfs)". if runtime.GOOS == "darwin" { mOpts.Options = append(mOpts.Options, "volname="+path.Base(args.mountpoint)) } - // The kernel enforces read-only operation, we just have to pass "ro". // Reverse mounts are always read-only. if args.ro || args.reverse { 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 // a chance to override them. diff --git a/tests/cli/cli_test.go b/tests/cli/cli_test.go index 8808742..5508360 100644 --- a/tests/cli/cli_test.go +++ b/tests/cli/cli_test.go @@ -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. // See also cli_args_test.go for comprehensive tests of "-o" parsing. func TestMissingOArg(t *testing.T) { diff --git a/tests/test_helpers/helpers.go b/tests/test_helpers/helpers.go index e7fcf4b..ef748e2 100644 --- a/tests/test_helpers/helpers.go +++ b/tests/test_helpers/helpers.go @@ -433,9 +433,14 @@ func ExtractCmdExitCode(err error) int { return 0 } // OMG this is convoluted - err2 := err.(*exec.ExitError) - code := err2.Sys().(syscall.WaitStatus).ExitStatus() - return code + if err2, ok := err.(*exec.ExitError); ok { + return err2.Sys().(syscall.WaitStatus).ExitStatus() + } + 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.