diff options
| -rw-r--r-- | cli_args.go | 22 | ||||
| -rw-r--r-- | daemonize.go | 4 | ||||
| -rw-r--r-- | mount.go | 20 | ||||
| -rw-r--r-- | tests/cli/cli_test.go | 24 | ||||
| -rw-r--r-- | tests/test_helpers/helpers.go | 11 | 
5 files changed, 66 insertions, 15 deletions
| 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. @@ -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. | 
