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 .
This commit is contained in:
Jakob Unterwurzacher 2017-05-07 22:15:01 +02:00
parent ad7942f434
commit d5adde1eeb
11 changed files with 106 additions and 66 deletions

View File

@ -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
}

View File

@ -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

View File

@ -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 := ""

View File

@ -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)
}
}

View File

@ -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

View File

@ -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

View File

@ -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 {

33
main.go
View File

@ -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 {

View File

@ -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 +

View File

@ -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)
}()
}

View File

@ -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)
}
}