Skip to content

Commit 53d6a99

Browse files
committed
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 #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
1 parent e29a81e commit 53d6a99

File tree

5 files changed

+66
-15
lines changed

5 files changed

+66
-15
lines changed

cli_args.go

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,11 @@ import (
2020
type argContainer struct {
2121
debug, init, zerokey, fusedebug, openssl, passwd, fg, version,
2222
plaintextnames, quiet, nosyslog, wpanic,
23-
longnames, allow_other, ro, reverse, aessiv, nonempty, raw64,
23+
longnames, allow_other, reverse, aessiv, nonempty, raw64,
2424
noprealloc, speed, hkdf, serialize_reads, forcedecode, hh, info,
2525
sharedstorage, devrandom, fsck bool
26+
// Mount options with opposites
27+
dev, nodev, suid, nosuid, exec, noexec, rw, ro bool
2628
masterkey, mountpoint, cipherdir, cpuprofile, extpass,
2729
memprofile, ko, passfile, ctlsock, fsname, force_owner, trace string
2830
// Configuration file name override
@@ -121,7 +123,6 @@ func parseCliOpts() (args argContainer) {
121123
flagSet.BoolVar(&args.longnames, "longnames", true, "Store names longer than 176 bytes in extra files")
122124
flagSet.BoolVar(&args.allow_other, "allow_other", false, "Allow other users to access the filesystem. "+
123125
"Only works if user_allow_other is set in /etc/fuse.conf.")
124-
flagSet.BoolVar(&args.ro, "ro", false, "Mount the filesystem read-only")
125126
flagSet.BoolVar(&args.reverse, "reverse", false, "Reverse mode")
126127
flagSet.BoolVar(&args.aessiv, "aessiv", false, "AES-SIV encryption")
127128
flagSet.BoolVar(&args.nonempty, "nonempty", false, "Allow mounting over non-empty directories")
@@ -137,6 +138,17 @@ func parseCliOpts() (args argContainer) {
137138
flagSet.BoolVar(&args.sharedstorage, "sharedstorage", false, "Make concurrent access to a shared CIPHERDIR safer")
138139
flagSet.BoolVar(&args.devrandom, "devrandom", false, "Use /dev/random for generating master key")
139140
flagSet.BoolVar(&args.fsck, "fsck", false, "Run a filesystem check on CIPHERDIR")
141+
142+
// Mount options with opposites
143+
flagSet.BoolVar(&args.dev, "dev", false, "Allow device files")
144+
flagSet.BoolVar(&args.nodev, "nodev", false, "Deny device files")
145+
flagSet.BoolVar(&args.suid, "suid", false, "Allow suid binaries")
146+
flagSet.BoolVar(&args.nosuid, "nosuid", false, "Deny suid binaries")
147+
flagSet.BoolVar(&args.exec, "exec", false, "Allow executables")
148+
flagSet.BoolVar(&args.noexec, "noexec", false, "Deny executables")
149+
flagSet.BoolVar(&args.rw, "rw", false, "Mount the filesystem read-write")
150+
flagSet.BoolVar(&args.ro, "ro", false, "Mount the filesystem read-only")
151+
140152
flagSet.StringVar(&args.masterkey, "masterkey", "", "Mount with explicit master key")
141153
flagSet.StringVar(&args.cpuprofile, "cpuprofile", "", "Write cpu profile to specified file")
142154
flagSet.StringVar(&args.memprofile, "memprofile", "", "Write memory profile to specified file")
@@ -152,12 +164,6 @@ func parseCliOpts() (args argContainer) {
152164
"successful mount - used internally for daemonization")
153165
flagSet.IntVar(&args.scryptn, "scryptn", configfile.ScryptDefaultLogN, "scrypt cost parameter logN. Possible values: 10-28. "+
154166
"A lower value speeds up mounting and reduces its memory needs, but makes the password susceptible to brute-force attacks")
155-
// Ignored otions
156-
var dummyBool bool
157-
ignoreText := "(ignored for compatibility)"
158-
flagSet.BoolVar(&dummyBool, "rw", false, ignoreText)
159-
flagSet.BoolVar(&dummyBool, "nosuid", false, ignoreText)
160-
flagSet.BoolVar(&dummyBool, "nodev", false, ignoreText)
161167
var dummyString string
162168
flagSet.StringVar(&dummyString, "o", "", "For compatibility with mount(1), options can be also passed as a comma-separated list to -o on the end.")
163169
// Actual parsing

daemonize.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ func forkChild() int {
3737
exitOnUsr1()
3838
err := c.Start()
3939
if err != nil {
40-
tlog.Fatal.Printf("forkChild: starting %s failed: %v\n", name, err)
40+
tlog.Fatal.Printf("forkChild: starting %s failed: %v", name, err)
4141
return exitcodes.ForkChild
4242
}
4343
err = c.Wait()
@@ -47,7 +47,7 @@ func forkChild() int {
4747
os.Exit(waitstat.ExitStatus())
4848
}
4949
}
50-
tlog.Fatal.Printf("forkChild: wait returned an unknown error: %v\n", err)
50+
tlog.Fatal.Printf("forkChild: wait returned an unknown error: %v", err)
5151
return exitcodes.ForkChild
5252
}
5353
// The child exited with 0 - let's do the same.

mount.go

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -314,17 +314,33 @@ func initGoFuse(fs pathfs.FileSystem, args *argContainer) *fuse.Server {
314314
if args.reverse {
315315
mOpts.Name += "-reverse"
316316
}
317-
318317
// Add a volume name if running osxfuse. Otherwise the Finder will show it as
319318
// something like "osxfuse Volume 0 (gocryptfs)".
320319
if runtime.GOOS == "darwin" {
321320
mOpts.Options = append(mOpts.Options, "volname="+path.Base(args.mountpoint))
322321
}
323-
324322
// The kernel enforces read-only operation, we just have to pass "ro".
325323
// Reverse mounts are always read-only.
326324
if args.ro || args.reverse {
327325
mOpts.Options = append(mOpts.Options, "ro")
326+
} else if args.rw {
327+
mOpts.Options = append(mOpts.Options, "rw")
328+
}
329+
// If both "nosuid" and "suid" were passed, the safer option wins.
330+
if args.nosuid {
331+
mOpts.Options = append(mOpts.Options, "nosuid")
332+
} else if args.suid {
333+
mOpts.Options = append(mOpts.Options, "suid")
334+
}
335+
if args.nodev {
336+
mOpts.Options = append(mOpts.Options, "nodev")
337+
} else if args.dev {
338+
mOpts.Options = append(mOpts.Options, "dev")
339+
}
340+
if args.noexec {
341+
mOpts.Options = append(mOpts.Options, "noexec")
342+
} else if args.exec {
343+
mOpts.Options = append(mOpts.Options, "exec")
328344
}
329345
// Add additional mount options (if any) after the stock ones, so the user has
330346
// a chance to override them.

tests/cli/cli_test.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -449,6 +449,30 @@ func TestMultipleOperationFlags(t *testing.T) {
449449
}
450450
}
451451

452+
func TestNoexec(t *testing.T) {
453+
dir := test_helpers.InitFS(t)
454+
mnt := dir + ".mnt"
455+
err := os.Mkdir(mnt, 0700)
456+
if err != nil {
457+
t.Fatal(err)
458+
}
459+
test_helpers.MountOrFatal(t, dir, mnt, "-extpass=echo test", "-noexec")
460+
defer test_helpers.UnmountPanic(mnt)
461+
sh := mnt + "/x.sh"
462+
content := `#!/bin/bash
463+
echo hello
464+
`
465+
err = ioutil.WriteFile(sh, []byte(content), 0755)
466+
if err != nil {
467+
t.Fatal(err)
468+
}
469+
err = exec.Command(sh).Run()
470+
exitCode := test_helpers.ExtractCmdExitCode(err)
471+
if exitCode != int(syscall.EACCES) {
472+
t.Errorf("got exitcode %d instead of EPERM (%d)", exitCode, syscall.EPERM)
473+
}
474+
}
475+
452476
// Test that a missing argument to "-o" triggers exit code 1.
453477
// See also cli_args_test.go for comprehensive tests of "-o" parsing.
454478
func TestMissingOArg(t *testing.T) {

tests/test_helpers/helpers.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -433,9 +433,14 @@ func ExtractCmdExitCode(err error) int {
433433
return 0
434434
}
435435
// OMG this is convoluted
436-
err2 := err.(*exec.ExitError)
437-
code := err2.Sys().(syscall.WaitStatus).ExitStatus()
438-
return code
436+
if err2, ok := err.(*exec.ExitError); ok {
437+
return err2.Sys().(syscall.WaitStatus).ExitStatus()
438+
}
439+
if err2, ok := err.(*os.PathError); ok {
440+
return int(err2.Err.(syscall.Errno))
441+
}
442+
log.Panicf("could not decode error %#v", err)
443+
return 0
439444
}
440445

441446
// ListFds lists our open file descriptors.

0 commit comments

Comments
 (0)