From d5adde1eeb13ba377f7c05b9f21893c01f61ec16 Mon Sep 17 00:00:00 2001 From: Jakob Unterwurzacher Date: Sun, 7 May 2017 22:15:01 +0200 Subject: exitcodes: pull all exit code definitions into the package This commit defines all exit codes in one place in the exitcodes package. Also, it adds a test to verify the exit code on incorrect password, which is what SiriKali cares about the most. Fixes https://github.com/rfjakob/gocryptfs/issues/77 . --- cli_args.go | 19 ++++++++++--------- daemonize.go | 5 +++-- init_dir.go | 9 +++++---- internal/configfile/scrypt.go | 11 ++++++----- internal/exitcodes/exitcodes.go | 31 +++++++++++++++++++++++++++++-- internal/readpassword/read.go | 24 ++++++++++++------------ internal/stupidgcm/without_openssl.go | 4 +++- main.go | 33 ++++++++++++--------------------- masterkey.go | 5 +++-- mount.go | 17 +++++++++-------- tests/cli/cli_test.go | 14 ++++++++++++++ 11 files changed, 106 insertions(+), 66 deletions(-) diff --git a/cli_args.go b/cli_args.go index e8fab75..be6d688 100644 --- a/cli_args.go +++ b/cli_args.go @@ -9,6 +9,7 @@ import ( "strings" "github.com/rfjakob/gocryptfs/internal/configfile" + "github.com/rfjakob/gocryptfs/internal/exitcodes" "github.com/rfjakob/gocryptfs/internal/prefer_openssl" "github.com/rfjakob/gocryptfs/internal/stupidgcm" "github.com/rfjakob/gocryptfs/internal/tlog" @@ -56,7 +57,7 @@ func prefixOArgs(osArgs []string) []string { // Last argument? if i+1 >= len(osArgs) { tlog.Fatal.Printf("The \"-o\" option requires an argument") - os.Exit(ErrExitUsage) + os.Exit(exitcodes.Usage) } oOpts = strings.Split(osArgs[i+1], ",") // Skip over the arguments to "-o" @@ -76,7 +77,7 @@ func prefixOArgs(osArgs []string) []string { } if o == "o" || o == "-o" { tlog.Fatal.Printf("You can't pass \"-o\" to \"-o\"") - os.Exit(ErrExitUsage) + os.Exit(exitcodes.Usage) } newArgs = append(newArgs, "-"+o) } @@ -153,7 +154,7 @@ func parseCliOpts() (args argContainer) { if err != nil { tlog.Warn.Printf("You passed: %s", prettyArgs()) tlog.Fatal.Printf("%v", err) - os.Exit(ErrExitUsage) + os.Exit(exitcodes.Usage) } // "-openssl" needs some post-processing if opensslAuto == "auto" { @@ -162,27 +163,27 @@ func parseCliOpts() (args argContainer) { args.openssl, err = strconv.ParseBool(opensslAuto) if err != nil { tlog.Fatal.Printf("Invalid \"-openssl\" setting: %v", err) - os.Exit(ErrExitUsage) + os.Exit(exitcodes.Usage) } } // "-forcedecode" only works with openssl. Check compilation and command line parameters if args.forcedecode == true { if stupidgcm.BuiltWithoutOpenssl == true { tlog.Fatal.Printf("The -forcedecode flag requires openssl support, but gocryptfs was compiled without it!") - os.Exit(ErrExitUsage) + os.Exit(exitcodes.Usage) } if args.aessiv == true { tlog.Fatal.Printf("The -forcedecode and -aessiv flags are incompatible because they use different crypto libs (openssl vs native Go)") - os.Exit(ErrExitUsage) + os.Exit(exitcodes.Usage) } if args.reverse == true { tlog.Fatal.Printf("The reverse mode and the -forcedecode option are not compatible") - os.Exit(ErrExitUsage) + os.Exit(exitcodes.Usage) } // Has the user explicitely disabled openssl using "-openssl=false/0"? if !args.openssl && opensslAuto != "auto" { tlog.Fatal.Printf("-forcedecode requires openssl, but is disabled via command-line option") - os.Exit(ErrExitUsage) + os.Exit(exitcodes.Usage) } args.openssl = true @@ -197,7 +198,7 @@ func parseCliOpts() (args argContainer) { } if args.extpass != "" && args.masterkey != "" { tlog.Fatal.Printf("The options -extpass and -masterkey cannot be used at the same time") - os.Exit(ErrExitUsage) + os.Exit(exitcodes.Usage) } return args } diff --git a/daemonize.go b/daemonize.go index 9869258..de09743 100644 --- a/daemonize.go +++ b/daemonize.go @@ -7,6 +7,7 @@ import ( "os/signal" "syscall" + "github.com/rfjakob/gocryptfs/internal/exitcodes" "github.com/rfjakob/gocryptfs/internal/tlog" ) @@ -36,7 +37,7 @@ func forkChild() int { err := c.Start() if err != nil { tlog.Fatal.Printf("forkChild: starting %s failed: %v\n", name, err) - return 1 + return exitcodes.ForkChild } err = c.Wait() if err != nil { @@ -46,7 +47,7 @@ func forkChild() int { } } tlog.Fatal.Printf("forkChild: wait returned an unknown error: %v\n", err) - return 1 + return exitcodes.ForkChild } // The child exited with 0 - let's do the same. return 0 diff --git a/init_dir.go b/init_dir.go index 7b1dc7b..65b25d1 100644 --- a/init_dir.go +++ b/init_dir.go @@ -6,6 +6,7 @@ import ( "strings" "github.com/rfjakob/gocryptfs/internal/configfile" + "github.com/rfjakob/gocryptfs/internal/exitcodes" "github.com/rfjakob/gocryptfs/internal/nametransform" "github.com/rfjakob/gocryptfs/internal/readpassword" "github.com/rfjakob/gocryptfs/internal/tlog" @@ -22,13 +23,13 @@ func initDir(args *argContainer) { _, err = os.Stat(args.config) if err == nil { tlog.Fatal.Printf("Config file %q already exists", args.config) - os.Exit(ErrExitInit) + os.Exit(exitcodes.Init) } } else { err = checkDirEmpty(args.cipherdir) if err != nil { tlog.Fatal.Printf("Invalid cipherdir: %v", err) - os.Exit(ErrExitInit) + os.Exit(exitcodes.Init) } } // Choose password for config file @@ -41,7 +42,7 @@ func initDir(args *argContainer) { err = configfile.CreateConfFile(args.config, password, args.plaintextnames, args.scryptn, creator, args.aessiv) if err != nil { tlog.Fatal.Println(err) - os.Exit(ErrExitInit) + os.Exit(exitcodes.Init) } // Forward mode with filename encryption enabled needs a gocryptfs.diriv // in the root dir @@ -49,7 +50,7 @@ func initDir(args *argContainer) { err = nametransform.WriteDirIV(args.cipherdir) if err != nil { tlog.Fatal.Println(err) - os.Exit(ErrExitInit) + os.Exit(exitcodes.Init) } } mountArgs := "" diff --git a/internal/configfile/scrypt.go b/internal/configfile/scrypt.go index 0646754..b5a3edb 100644 --- a/internal/configfile/scrypt.go +++ b/internal/configfile/scrypt.go @@ -8,6 +8,7 @@ import ( "golang.org/x/crypto/scrypt" "github.com/rfjakob/gocryptfs/internal/cryptocore" + "github.com/rfjakob/gocryptfs/internal/exitcodes" "github.com/rfjakob/gocryptfs/internal/tlog" ) @@ -84,22 +85,22 @@ func (s *ScryptKDF) validateParams() { minN := 1 << scryptMinLogN if s.N < minN { tlog.Fatal.Println("Fatal: scryptn below 10 is too low to make sense") - os.Exit(1) + os.Exit(exitcodes.ScryptParams) } if s.R < scryptMinR { tlog.Fatal.Printf("Fatal: scrypt parameter R below minimum: value=%d, min=%d", s.R, scryptMinR) - os.Exit(1) + os.Exit(exitcodes.ScryptParams) } if s.P < scryptMinP { tlog.Fatal.Printf("Fatal: scrypt parameter P below minimum: value=%d, min=%d", s.P, scryptMinP) - os.Exit(1) + os.Exit(exitcodes.ScryptParams) } if len(s.Salt) < scryptMinSaltLen { tlog.Fatal.Printf("Fatal: scrypt salt length below minimum: value=%d, min=%d", len(s.Salt), scryptMinSaltLen) - os.Exit(1) + os.Exit(exitcodes.ScryptParams) } if s.KeyLen < cryptocore.KeyLen { tlog.Fatal.Printf("Fatal: scrypt parameter KeyLen below minimum: value=%d, min=%d", len(s.Salt), cryptocore.KeyLen) - os.Exit(1) + os.Exit(exitcodes.ScryptParams) } } diff --git a/internal/exitcodes/exitcodes.go b/internal/exitcodes/exitcodes.go index 2ec3ae6..7a2efdb 100644 --- a/internal/exitcodes/exitcodes.go +++ b/internal/exitcodes/exitcodes.go @@ -8,12 +8,39 @@ import ( ) const ( + // Usage error: cli syntax etc + Usage = 1 + // 2 is reserved because it is used by Go panic + + // Mount is an error on mount + Mount = 3 + // CipherDir means that the CIPHERDIR does not exist + CipherDir = 6 + // Init is an error on filesystem init + Init = 7 + // LoadConf is an error while loading gocryptfs.conf + LoadConf = 8 + // ReadPassword means something went wrong reading the password + ReadPassword = 9 + // MountPoint error means that the mountpoint is invalid (not empty etc). + MountPoint = 10 // Other error - please inspect the message Other = 11 // PasswordIncorrect - the password was incorrect PasswordIncorrect = 12 - // TODO several other exit codes are defined in main.go. These will be - // ported over here. + // ScryptParams means that scrypt was called with invalid parameters + ScryptParams = 13 + // MasterKey means that something went wrong when parsing the "-masterkey" + // command line option + MasterKey = 14 + // SigInt means we got SIGINT + SigInt = 15 + // PanicLog means the panic log was not empty when we were unmounted + PanicLog = 16 + // ForkChild means forking the worker child failed + ForkChild = 17 + // OpenSSL means you tried to enable OpenSSL, but we were compiled without it. + OpenSSL = 18 ) // Err wraps an error with an associated numeric exit code diff --git a/internal/readpassword/read.go b/internal/readpassword/read.go index 563c62d..058735d 100644 --- a/internal/readpassword/read.go +++ b/internal/readpassword/read.go @@ -12,11 +12,11 @@ import ( "golang.org/x/crypto/ssh/terminal" + "github.com/rfjakob/gocryptfs/internal/exitcodes" "github.com/rfjakob/gocryptfs/internal/tlog" ) const ( - exitCode = 9 // 2kB limit like EncFS maxPasswordLen = 2048 ) @@ -46,7 +46,7 @@ func Twice(extpass string) string { p2 := readPasswordTerminal("Repeat: ") if p1 != p2 { tlog.Fatal.Println("Passwords do not match") - os.Exit(exitCode) + os.Exit(exitcodes.ReadPassword) } return p1 } @@ -60,12 +60,12 @@ func readPasswordTerminal(prompt string) string { p, err := terminal.ReadPassword(fd) if err != nil { tlog.Fatal.Printf("Could not read password from terminal: %v\n", err) - os.Exit(exitCode) + os.Exit(exitcodes.ReadPassword) } fmt.Fprintf(os.Stderr, "\n") if len(p) == 0 { tlog.Fatal.Println("Password is empty") - os.Exit(exitCode) + os.Exit(exitcodes.ReadPassword) } return string(p) } @@ -77,7 +77,7 @@ func readPasswordStdin() string { p := readLineUnbuffered(os.Stdin) if len(p) == 0 { tlog.Fatal.Println("Got empty password from stdin") - os.Exit(exitCode) + os.Exit(exitcodes.ReadPassword) } return p } @@ -102,23 +102,23 @@ func readPasswordExtpass(extpass string) string { pipe, err := cmd.StdoutPipe() if err != nil { tlog.Fatal.Printf("extpass pipe setup failed: %v", err) - os.Exit(exitCode) + os.Exit(exitcodes.ReadPassword) } err = cmd.Start() if err != nil { tlog.Fatal.Printf("extpass cmd start failed: %v", err) - os.Exit(exitCode) + os.Exit(exitcodes.ReadPassword) } p := readLineUnbuffered(pipe) pipe.Close() err = cmd.Wait() if err != nil { tlog.Fatal.Printf("extpass program returned an error: %v", err) - os.Exit(exitCode) + os.Exit(exitcodes.ReadPassword) } if len(p) == 0 { tlog.Fatal.Println("extpass: password is empty") - os.Exit(exitCode) + os.Exit(exitcodes.ReadPassword) } return p } @@ -130,7 +130,7 @@ func readLineUnbuffered(r io.Reader) (l string) { for { if len(l) > maxPasswordLen { tlog.Fatal.Printf("fatal: maximum password length of %d bytes exceeded", maxPasswordLen) - os.Exit(exitCode) + os.Exit(exitcodes.ReadPassword) } n, err := r.Read(b) if err == io.EOF { @@ -138,7 +138,7 @@ func readLineUnbuffered(r io.Reader) (l string) { } if err != nil { tlog.Fatal.Printf("readLineUnbuffered: %v", err) - os.Exit(exitCode) + os.Exit(exitcodes.ReadPassword) } if n == 0 { continue @@ -170,7 +170,7 @@ func CheckTrailingGarbage() { n, _ := os.Stdin.Read(b) if n > 0 { tlog.Fatal.Printf("Received trailing garbage after the password") - os.Exit(exitCode) + os.Exit(exitcodes.ReadPassword) } }() // Wait for the goroutine to start up plus one millisecond for the read to diff --git a/internal/stupidgcm/without_openssl.go b/internal/stupidgcm/without_openssl.go index 1c6ebcf..efd37f3 100644 --- a/internal/stupidgcm/without_openssl.go +++ b/internal/stupidgcm/without_openssl.go @@ -5,6 +5,8 @@ package stupidgcm import ( "fmt" "os" + + "github.com/rfjakob/gocryptfs/internal/exitcodes" ) type stupidGCM struct{} @@ -16,7 +18,7 @@ const ( func errExit() { fmt.Fprintln(os.Stderr, "gocryptfs has been compiled without openssl support but you are still trying to use openssl") - os.Exit(2) + os.Exit(exitcodes.OpenSSL) } func New(_ []byte, _ bool) stupidGCM { diff --git a/main.go b/main.go index 160829f..e522f6b 100644 --- a/main.go +++ b/main.go @@ -12,22 +12,13 @@ import ( "github.com/rfjakob/gocryptfs/internal/configfile" "github.com/rfjakob/gocryptfs/internal/contentenc" + "github.com/rfjakob/gocryptfs/internal/exitcodes" "github.com/rfjakob/gocryptfs/internal/readpassword" "github.com/rfjakob/gocryptfs/internal/speed" "github.com/rfjakob/gocryptfs/internal/stupidgcm" "github.com/rfjakob/gocryptfs/internal/tlog" ) -// Exit codes -const ( - ErrExitUsage = 1 - ErrExitMount = 3 - ErrExitCipherDir = 6 - ErrExitInit = 7 - ErrExitLoadConf = 8 - ErrExitMountPoint = 10 -) - // GitVersion is the gocryptfs version according to git, set by build.bash var GitVersion = "[GitVersion not set - please compile using ./build.bash]" @@ -85,7 +76,7 @@ func loadConfig(args *argContainer) (masterkey []byte, confFile *configfile.Conf func changePassword(args *argContainer) { masterkey, confFile, err := loadConfig(args) if err != nil { - os.Exit(ErrExitLoadConf) + os.Exit(exitcodes.LoadConf) } tlog.Info.Println("Please enter your new password.") newPw := readpassword.Twice(args.extpass) @@ -96,7 +87,7 @@ func changePassword(args *argContainer) { err = os.Link(args.config, bak) if err != nil { tlog.Fatal.Printf("Could not create backup file: %v", err) - os.Exit(ErrExitInit) + os.Exit(exitcodes.Init) } tlog.Info.Printf(tlog.ColorGrey+ "A copy of the old config file has been created at %q.\n"+ @@ -106,7 +97,7 @@ func changePassword(args *argContainer) { err = confFile.WriteFile() if err != nil { tlog.Fatal.Println(err) - os.Exit(ErrExitInit) + os.Exit(exitcodes.Init) } tlog.Info.Printf(tlog.ColorGreen + "Password changed." + tlog.ColorReset) os.Exit(0) @@ -169,11 +160,11 @@ func main() { err = checkDir(args.cipherdir) if err != nil { tlog.Fatal.Printf("Invalid cipherdir: %v", err) - os.Exit(ErrExitCipherDir) + os.Exit(exitcodes.CipherDir) } } else { usageText() - os.Exit(ErrExitUsage) + os.Exit(exitcodes.Usage) } // "-q" if args.quiet { @@ -188,7 +179,7 @@ func main() { args.config, err = filepath.Abs(args.config) if err != nil { tlog.Fatal.Printf("Invalid \"-config\" setting: %v", err) - os.Exit(ErrExitInit) + os.Exit(exitcodes.Init) } tlog.Info.Printf("Using config file at custom location %s", args.config) args._configCustom = true @@ -204,7 +195,7 @@ func main() { f, err = os.Create(args.cpuprofile) if err != nil { tlog.Fatal.Println(err) - os.Exit(ErrExitInit) + os.Exit(exitcodes.Init) } pprof.StartCPUProfile(f) defer pprof.StopCPUProfile() @@ -216,7 +207,7 @@ func main() { f, err = os.Create(args.memprofile) if err != nil { tlog.Fatal.Println(err) - os.Exit(ErrExitInit) + os.Exit(exitcodes.Init) } defer func() { pprof.WriteHeapProfile(f) @@ -238,7 +229,7 @@ func main() { if args.init { if flagSet.NArg() > 1 { tlog.Fatal.Printf("Usage: %s -init [OPTIONS] CIPHERDIR", tlog.ProgramName) - os.Exit(ErrExitUsage) + os.Exit(exitcodes.Usage) } initDir(&args) // does not return } @@ -246,7 +237,7 @@ func main() { if args.passwd { if flagSet.NArg() > 1 { tlog.Fatal.Printf("Usage: %s -passwd [OPTIONS] CIPHERDIR", tlog.ProgramName) - os.Exit(ErrExitUsage) + os.Exit(exitcodes.Usage) } changePassword(&args) // does not return } @@ -256,7 +247,7 @@ func main() { tlog.Info.Printf("Wrong number of arguments (have %d, want 2). You passed: %s", flagSet.NArg(), prettyArgs) tlog.Fatal.Printf("Usage: %s [OPTIONS] CIPHERDIR MOUNTPOINT [-o COMMA-SEPARATED-OPTIONS]", tlog.ProgramName) - os.Exit(ErrExitUsage) + os.Exit(exitcodes.Usage) } ret := doMount(&args) if ret != 0 { diff --git a/masterkey.go b/masterkey.go index d943745..3d7afd1 100644 --- a/masterkey.go +++ b/masterkey.go @@ -8,6 +8,7 @@ import ( "golang.org/x/crypto/ssh/terminal" "github.com/rfjakob/gocryptfs/internal/cryptocore" + "github.com/rfjakob/gocryptfs/internal/exitcodes" "github.com/rfjakob/gocryptfs/internal/tlog" ) @@ -50,11 +51,11 @@ func parseMasterKey(masterkey string) []byte { key, err := hex.DecodeString(masterkey) if err != nil { tlog.Fatal.Printf("Could not parse master key: %v", err) - os.Exit(1) + os.Exit(exitcodes.MasterKey) } if len(key) != cryptocore.KeyLen { tlog.Fatal.Printf("Master key has length %d but we require length %d", len(key), cryptocore.KeyLen) - os.Exit(1) + os.Exit(exitcodes.MasterKey) } tlog.Info.Printf("Using explicit master key.") tlog.Info.Printf(tlog.ColorYellow + diff --git a/mount.go b/mount.go index b85692c..bcf056d 100644 --- a/mount.go +++ b/mount.go @@ -36,14 +36,14 @@ func doMount(args *argContainer) int { args.mountpoint, err = filepath.Abs(flagSet.Arg(1)) if err != nil { tlog.Fatal.Printf("Invalid mountpoint: %v", err) - os.Exit(ErrExitMountPoint) + os.Exit(exitcodes.MountPoint) } // We cannot mount "/home/user/.cipher" at "/home/user" because the mount // will hide ".cipher" also for us. if args.cipherdir == args.mountpoint || strings.HasPrefix(args.cipherdir, args.mountpoint+"/") { tlog.Fatal.Printf("Mountpoint %q would shadow cipherdir %q, this is not supported", args.mountpoint, args.cipherdir) - os.Exit(ErrExitMountPoint) + os.Exit(exitcodes.MountPoint) } if args.nonempty { err = checkDir(args.mountpoint) @@ -52,7 +52,7 @@ func doMount(args *argContainer) int { } if err != nil { tlog.Fatal.Printf("Invalid mountpoint: %v", err) - os.Exit(ErrExitMountPoint) + os.Exit(exitcodes.MountPoint) } // Open control socket early so we can error out before asking the user // for the password @@ -64,7 +64,7 @@ func doMount(args *argContainer) int { sock, err = net.Listen("unix", args.ctlsock) if err != nil { tlog.Fatal.Printf("ctlsock: %v", err) - os.Exit(ErrExitMount) + os.Exit(exitcodes.Mount) } args._ctlsockFd = sock // Close also deletes the socket file @@ -118,7 +118,7 @@ func doMount(args *argContainer) int { paniclog, err = ioutil.TempFile("", "gocryptfs_paniclog.") if err != nil { tlog.Fatal.Printf("Failed to create gocryptfs_paniclog: %v", err) - os.Exit(ErrExitMount) + os.Exit(exitcodes.Mount) } // Switch all of our logs and the generic logger to syslog tlog.Info.SwitchToSyslog(syslog.LOG_USER | syslog.LOG_INFO) @@ -165,6 +165,7 @@ func doMount(args *argContainer) int { } else if fi.Size() > 0 { tlog.Warn.Printf("paniclog at %q is not empty (size %d). Not deleting it.", paniclog.Name(), fi.Size()) + return exitcodes.PanicLog } else { syscall.Unlink(paniclog.Name()) } @@ -227,7 +228,7 @@ func initFuseFrontend(key []byte, args *argContainer, confFile *configfile.ConfF frontendArgs.CryptoBackend = cryptocore.BackendAESSIV } else if args.reverse { tlog.Fatal.Printf("AES-SIV is required by reverse mode, but not enabled in the config file") - os.Exit(ErrExitUsage) + os.Exit(exitcodes.Usage) } } // If allow_other is set and we run as root, try to give newly created files to @@ -306,7 +307,7 @@ func initFuseFrontend(key []byte, args *argContainer, confFile *configfile.ConfF srv, err := fuse.NewServer(conn.RawFS(), args.mountpoint, &mOpts) if err != nil { tlog.Fatal.Printf("Mount failed: %v", err) - os.Exit(ErrExitMount) + os.Exit(exitcodes.Mount) } srv.SetDebug(args.fusedebug) @@ -336,6 +337,6 @@ func handleSigint(srv *fuse.Server, mountpoint string) { cmd.Run() } } - os.Exit(1) + os.Exit(exitcodes.SigInt) }() } diff --git a/tests/cli/cli_test.go b/tests/cli/cli_test.go index cce7fa0..905a890 100644 --- a/tests/cli/cli_test.go +++ b/tests/cli/cli_test.go @@ -10,6 +10,7 @@ import ( "testing" "github.com/rfjakob/gocryptfs/internal/configfile" + "github.com/rfjakob/gocryptfs/internal/exitcodes" "github.com/rfjakob/gocryptfs/tests/test_helpers" ) @@ -316,3 +317,16 @@ func TestInitTrailingGarbage(t *testing.T) { } } } + +// TestPasswordIncorrect makes sure the correct exit code is used when the password +// was incorrect +func TestPasswordIncorrect(t *testing.T) { + cDir := test_helpers.InitFS(t) + pDir := cDir + ".mnt" + err := test_helpers.Mount(cDir, pDir, false, "-extpass", "echo WRONG", "-wpanic=false") + // vvvvvvvvvvvvvv OMG vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv + exitCode := err.(*exec.ExitError).Sys().(syscall.WaitStatus).ExitStatus() + if exitCode != exitcodes.PasswordIncorrect { + t.Errorf("want=%d, got=%d", exitcodes.PasswordIncorrect, exitCode) + } +} -- cgit v1.2.3